Кодер, вы разработаете торговую систему (багажник)?

сервер

пройти черезПредыдущая статьяАнализ, мы определили для этой системы. Ниже приведены настоящие сухие сделки. После тщательного прочтения содержания этих захвата я считаю, что вы можете разработать в качестве основы для хорошего обслуживания, масштабируемости хорошей торговой системы.

Дизайн базы данных

Данные оформляются по: транзакциям, возвратам, логам. Нет такой функции, как упомянутая выше сверка. Эту часть нетрудно спроектировать каждому самостоятельно, в соответствии с упомянутыми выше идеями. Основные таблицы описываются следующим образом:

  • pay_transactionЗапишите все данные о транзакции.
  • pay_transaction_extensionЗапишите номер транзакции, генерируемый каждый раз, когда транзакция инициируется третьей стороной.
  • pay_log_dataВсе данные журнала, такие как: запросы на оплату, запросы возврата, асинхронные уведомления и т. Д.
  • pay_repeat_transactionДанные о регулярных платежах
  • pay_notify_app_logУведомить журнал приложения
  • pay_refundЗапишите все данные о возврате

Конкретная структура таблицы:

-- -----------------------------------------------------
-- Table 创建支付流水表
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_transaction` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `app_id` VARCHAR(32) NOT NULL COMMENT '应用id',
  `pay_method_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付方式id,可以用来识别支付,如:支付宝、微信、Paypal等',
  `app_order_id` VARCHAR(64) NOT NULL COMMENT '应用方订单号',
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '本次交易唯一id,整个支付系统唯一,生成他的原因主要是 order_id对于其它应用来说可能重复',
  `total_fee` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付金额,整数方式保存',
  `scale` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '金额对应的小数位数',
  `currency_code` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '交易的币种',
  `pay_channel` VARCHAR(64) NOT NULL COMMENT '选择的支付渠道,比如:支付宝中的花呗、信用卡等',
  `expire_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单过期时间',
  `return_url` VARCHAR(255) NOT NULL COMMENT '支付后跳转url',
  `notify_url` VARCHAR(255) NOT NULL COMMENT '支付后,异步通知url',
  `email` VARCHAR(64) NOT NULL COMMENT '用户的邮箱',
  `sing_type` VARCHAR(10) NOT NULL DEFAULT 'RSA' COMMENT '采用的签方式:MD5 RSA RSA2 HASH-MAC等',
  `intput_charset` CHAR(5) NOT NULL DEFAULT 'UTF-8' COMMENT '字符集编码方式',
  `payment_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '第三方支付成功的时间',
  `notify_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '收到异步通知的时间',
  `finish_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '通知上游系统的时间',
  `trade_no` VARCHAR(64) NOT NULL COMMENT '第三方的流水号',
  `transaction_code` VARCHAR(64) NOT NULL COMMENT '真实给第三方的交易code,异步通知的时候更新',
  `order_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0:等待支付,1:待付款完成, 2:完成支付,3:该笔交易已关闭,-1:支付失败',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间',
  `update_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间',
  `create_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建的ip,这可能是自己服务的ip',
  `update_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新的ip',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `uniq_tradid` (`transaction_id`),
  INDEX `idx_trade_no` (`trade_no`),
  INDEX `idx_ctime` (`create_at`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '发起支付的数据';

-- -----------------------------------------------------
-- Table 交易扩展表
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_transaction_extension` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '系统唯一交易id',
  `pay_method_id` INT UNSIGNED NOT NULL DEFAULT 0,
  `transaction_code` VARCHAR(64) NOT NULL COMMENT '生成传输给第三方的订单号',
  `call_num` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '发起调用的次数',
  `extension_data` TEXT NOT NULL COMMENT '扩展内容,需要保存:transaction_code 与 trade no 的映射关系,异步通知的时候填充',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间',
  `create_ip` INT UNSIGNED NOT NULL COMMENT '创建ip',
  PRIMARY KEY (`id`),
  INDEX `idx_trads` (`transaction_id`, `pay_status`),
  UNIQUE INDEX `uniq_code` (`transaction_code`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '交易扩展表';

-- -----------------------------------------------------
-- Table 交易系统全部日志
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_log_data` (
  `id` BIGINT UNSIGNED NOT NULL,
  `app_id` VARCHAR(32) NOT NULL COMMENT '应用id',
  `app_order_id` VARCHAR(64) NOT NULL COMMENT '应用方订单号',
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '本次交易唯一id,整个支付系统唯一,生成他的原因主要是 order_id对于其它应用来说可能重复',
  `request_header` TEXT NOT NULL COMMENT '请求的header 头',
  `request_params` TEXT NOT NULL COMMENT '支付的请求参数',
  `log_type` VARCHAR(10) NOT NULL COMMENT '日志类型,payment:支付; refund:退款; notify:异步通知; return:同步通知; query:查询',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间',
  `create_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建ip',
  PRIMARY KEY (`id`),
  INDEX `idx_tradt` (`transaction_id`, `log_type`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '交易日志表';


-- -----------------------------------------------------
-- Table 重复支付的交易
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_repeat_transaction` (
  `id` BIGINT UNSIGNED NOT NULL,
  `app_id` VARCHAR(32) NOT NULL COMMENT '应用的id',
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '系统唯一识别交易号',
  `transaction_code` VARCHAR(64) NOT NULL COMMENT '支付成功时,该笔交易的 code',
  `trade_no` VARCHAR(64) NOT NULL COMMENT '第三方对应的交易号',
  `pay_method_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付方式',
  `total_fee` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '交易金额',
  `scale` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小数位数',
  `currency_code` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '支付选择的币种,CNY、HKD、USD等',
  `payment_time` INT NOT NULL COMMENT '第三方交易时间',
  `repeat_type` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '重复类型:1同渠道支付、2不同渠道支付',
  `repeat_status` TINYINT UNSIGNED DEFAULT 0 COMMENT '处理状态,0:未处理;1:已处理',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间',
  `update_at` INT UNSIGNED NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  INDEX `idx_trad` ( `transaction_id`),
  INDEX `idx_method` (`pay_method_id`),
  INDEX `idx_time` (`create_at`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '记录重复支付';


-- -----------------------------------------------------
-- Table 通知上游应用日志
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_notify_app_log` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `app_id` VARCHAR(32) NOT NULL COMMENT '应用id',
  `pay_method_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付方式',
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '交易号',
  `transaction_code` VARCHAR(64) NOT NULL COMMENT '支付成功时,该笔交易的 code',
  `sign_type` VARCHAR(10) NOT NULL DEFAULT 'RSA' COMMENT '采用的签名方式:MD5 RSA RSA2 HASH-MAC等',
  `input_charset` CHAR(5) NOT NULL DEFAULT 'UTF-8',
  `total_fee` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '涉及的金额,无小数',
  `scale` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小数位数',
  `pay_channel` VARCHAR(64) NOT NULL COMMENT '支付渠道',
  `trade_no` VARCHAR(64) NOT NULL COMMENT '第三方交易号',
  `payment_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付时间',
  `notify_type` VARCHAR(10) NOT NULL DEFAULT 'paid' COMMENT '通知类型,paid/refund/canceled',
  `notify_status` VARCHAR(7) NOT NULL DEFAULT 'INIT' COMMENT '通知支付调用方结果;INIT:初始化,PENDING: 进行中;  SUCCESS:成功;  FAILED:失败',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0,
  `update_at` INT UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  INDEX `idx_trad` (`transaction_id`),
  INDEX `idx_app` (`app_id`, `notify_status`)
  INDEX `idx_time` (`create_at`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '支付调用方记录';


-- -----------------------------------------------------
-- Table 退款
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `pay_refund` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `app_id` VARCHAR(64) NOT NULL COMMENT '应用id',
  `app_refund_no` VARCHAR(64) NOT NULL COMMENT '上游的退款id',
  `transaction_id` VARCHAR(64) NOT NULL COMMENT '交易号',
  `trade_no` VARCHAR(64) NOT NULL COMMENT '第三方交易号',
  `refund_no` VARCHAR(64) NOT NULL COMMENT '支付平台生成的唯一退款单号',
  `pay_method_id` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付方式',
  `pay_channel` VARCHAR(64) NOT NULL COMMENT '选择的支付渠道,比如:支付宝中的花呗、信用卡等',
  `refund_fee` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '退款金额',
  `scale` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小数位数',
  `refund_reason` VARCHAR(128) NOT NULL COMMENT '退款理由',
  `currency_code` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,CNY  USD HKD',
  `refund_type` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '退款类型;0:业务退款; 1:重复退款',
  `refund_method` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '退款方式:1自动原路返回; 2人工打款',
  `refund_status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0未退款; 1退款处理中; 2退款成功; 3退款不成功',
  `create_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间',
  `update_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间',
  `create_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '请求源ip',
  `update_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '请求源ip',
  PRIMARY KEY (`id`),
  UNIQUE INDEX `uniq_refno` (`refund_no`),
  INDEX `idx_trad` (`transaction_id`),
  INDEX `idx_status` (`refund_status`),
  INDEX `idx_ctime` (`create_at`)),
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COMMENT = '退款记录';

Логика использования таблицы кратко описана ниже:

платитьСначала нужно логировать запросыpay_log_dataА затем для создания записи данных транзакцииpay_transactionиpay_transaction_extensionсередина.

получить уведомление, записать данные вpay_log_data, а затем обновить его в соответствии с уведомлением об оплате или уведомлением о возвратеpay_transactionиpay_refundстатус. Если это повторный платеж, вам необходимо записать данные вpay_repeat_transactionсередина. и регистрируйте данные, которые должны быть отправлены в приложение, чтобыpay_notify_app_log, эта таблица эквивалентна таблице сообщений, и будут потребители, которые будут потреблять контент.

Возвращать деньгивойти в журналpay_log_data, а затем запишите данные в таблицу возвратаpay_refund.

Конечно, некоторые детали которых, нам всем нужно прочитать структуру таблицы, как подумать о фактическом использовании. Если у вас есть какие-либо вопросы, добро пожаловать в наш проект GitHub (нажмите, чтобы прочитать оригинальный текст) в сообщении, и мы ответим на него.

Эти таблицы могут удовлетворить самые основные потребности, а другой контент может быть расширен в соответствии с собственными потребностями, такими как поддержка списков карт пользователей, возврат средств с банковских карт и т. д.

Системный дизайн

Эта часть говорит, как система здания, а также рекомендации Организации Кодекса.

структура системы

Так как безопасность платежной системы очень высока, не рекомендуется напрямую выставлять соответствующий вход пользователю. Следует вызвать интерфейс платежной системы в собственной прикладной системе для завершения дела. Кроме того, к данным системы предъявляются следующие требования: строгая согласованность. Таким образом, буферизация не задействована (если буферизация может использоваться для оповещения, это не входит в область действия этого бита).

image-20190309135800643

Специфическая реализация, система использует два доменных имени, один для внутреннего использования, только указанный исходный IP-фиксирующий функцию для доступа (доступа к другим функциям в дополнение к уведомлению). Еще одно доменное имя может получить доступ только кnotify returnдва маршрута. Таким образом, мы можем гарантировать безопасность системы.

В использовании базы данных, независимо от того, что запрос идет напрямуюMasterбиблиотека. Это обеспечивает надежную согласованность данных. Конечно, библиотека тоже нужна. Например, мы можем использовать подчиненную библиотеку для завершения логики, связанной с выставлением счетов и согласованием.

дизайн кода

不管想做什么最终都要用代码来实现。我们都知道需要可维护、可扩展的代码。那么具体到支付系统你会怎么做呢?我已支付为例说下我的代码结构设计思路。 только для справки.比如我要介入:微信、支付宝、招行 三家支付。我的代码结构图如下:

image-20190309142925499

Кратко опишите его словами. Я бы инкапсулировал каждую третью сторону в:XXXGatewayКласс, который просто инкапсулирует сторонний интерфейс, обрабатывается внутри независимо от того, является ли другая сторона HTTP-запросом или SOAP-запросом.

Есть еще один слойXXXProxyЧтобы инкапсулировать эти возможности, предоставляемые третьими сторонами. Этот уровень в основном выполняет две функции: передачу данных по запросу на оплату для персонализации. Структура возвращается унифицированный процесс возвращается к унифицированной структуре верхней. Конечно, могут быть особые обстоятельства в соответствии со всеми процессами обслуживания;

Благодаря вышеописанной операции изменения были в основном полностью инкапсулированы. Если вы добавите платежный канал. Просто добавь:XXXGatewayиXXXProxy.

ТакContextиServerв чем смысл?Server内部封装了所有的业务逻辑,它提供接口给 action 或者其它 server 进行调用。 иContextЗначение этого слоя состоит в том, чтобы иметь дело сProxyОшибка, возвращаемая слоем. И обработка, связанная с сигналом тревоги, выполняется здесь.

Приведенная выше структура — лишь одна из моих практик, добро пожаловать на обсуждение.

Система, описанная в этой статье, удовлетворяет только самые основные платежные потребности. Отсутствие соответствующего мониторинга, сигнализации. Вы можете прийти к намСообщение на главной странице GitHub

Личный общедоступный номер:dayuTalk

Контактный адрес электронной почты: dayugog@gmail.com

Гитхаб:github.com/helei112g