0. Слова, написанные впереди
Платежная система — старая тема, и я также считаю, что платежная система, разработанная каждой компанией, отличается, потому что бизнес-форма не одинакова.
Здесь я не хочу говорить о большой и всеобъемлющей платежной системе, и у меня нет возможности объяснить это лично.
На мой взгляд, платежная система должна обеспечивать управление платежными каналами, платежные шлюзы, базовые возможности оплаты/возврата/перевода, платежные записи/подробности, а также соответствующие системы мониторинга и обслуживания.
Что касается так называемых расчетов по счетам, функции сверки счетов, системы счетов, системы контроля рисков и управления денежными потоками, то они должны быть включены в «финансовую систему». широкий смысл!
И сегодня я говорю только о «платежной системе» в узком смысле.
В настоящее время процесс оплаты включает три части: инициирование платежа, инициирование возврата и получение обратного вызова.
Принимая во внимание пропускную способность, программа изменила способ исходного синхронного асинхронного программирования, поэтому не случайно будет использоваться Java8 ExecutorService и CompletableFuture.
Кроме того, используются другие готовые вещи от компании: RabbitMQ, Redis, MongoDB.
Я намерен разработать эту платежную систему так, чтобы она не имела отношения к конкретному бизнесу и могла быть включена в общедоступную платформу компании.
Как это сделать, читайте дальше.
1. Инициировать платеж
В этой части описывается взаимодействие клиента и сервера для выполнения платежного запроса. У сервера должно быть ощущение, что именно клиент в конечном итоге инициирует платеж, и сервер предоставляет некоторую необходимую информацию о конфигурации параметров.
Схема архитектуры инициирования платежа выглядит следующим образом:
С отмеченным серийным номером вы можете отслеживать, как инициируется запрос платежа (диаграмма последовательности исключена).Процесс описывается следующим образом:
- Отправить платежное задание.Когда клиенту необходимо инициировать платеж, он начинается с добавления нового платежного задания в очередь платежных заданий.Этот процесс реализован асинхронно. Сначала построить новое платежное задание по параметрам, представленным клиентом;
- Предложите задачу, откройте асинхронную задачу, что она делает, так это добавляет новую платежную задачу в MQ, ожидающую использования;
- Описание платежной задачи. После успешного создания асинхронной задачи информация о платежной задаче, созданная на первом этапе, будет возвращена непосредственно клиенту;
- Опрос задачи, при этом потребитель платежной задачи опрашивает новую платежную задачу на выполнение;
- Отправьте запрос на оплату, этот шаг необходимо определить в соответствии с реальной ситуацией. Не все платежные запросы должны проходить через стороннюю платежную платформу, такую как Alipay, для WeChat необходимо подать заявку на prepay_id на основе параметров платежа, а затем инициировать платеж через клиент;
- Ответ, нечего сказать, необходимые параметры для оплаты, возвращаемой сторонними каналами;
- Кэшировать результат, на этом этапе платежное задание можно считать выполненным, а результат выполнения задания (независимо от того, успешно оно или нет) можно закешировать в Redis, ожидая ответного визита клиента в любой момент;
- Результат запроса, после того как клиент отправляет платежное задание, через определенный интервал (рекомендуется 2-3 секунды) он инициирует запрос на запрос результата;
- Запрос, перейдите непосредственно к Redis, чтобы найти результаты;
- Синхронизация, которая является асинхронной операцией, синхронизирует результат выполнения платежной задачи с MongoDB «между прочим» и удаляет результат выполнения задачи, кэшированный в Redis. Персистентность для MongoDB в основном предназначена для предоставления посадочного источника данных для последующей отказоустойчивости, повторных попыток, анализа данных и т. д.;
- Возврат, возвращаемый Redis серверу приложений;
- Возврат платежа, сервер приложений возвращает окончательный платежный объект клиенту.
Давайте немного углубимся, давайте посмотрим на три диаграммы классов:
① Давайте сначала поговорим о части PayTask. И PayTask, и Payment являются объектами Document в MongoDB, но во время выполнения задачи PayTask кэшируется Redis, что удобно для клиента, чтобы в любой момент инициировать Query.После успешного выполнения задачи будет сгенерирован объект Payment, и оба PayTask и Payment будут длиться вечно в MongoDB. В PayService есть несколько базовых операций над платежными задачами, включая отправку задачи, отмену, повторную попытку, сборку и так далее.
② Поговорим о выполнении задачи (раннера). Эта часть тесно связана с RabbitMQ.После формирования платежного задания оно будет поставлено в очередь на выполнение задания, откуда будет выведено и выполнено потребителем. В TaskRunner есть два основных метода интерфейса: run(task) и retry(task), которые выполняют задачи и повторяют задачи соответственно. Эти два метода были инкапсулированы в AbstractPayTaskRunner.Наследование AbstractPayTaskRunner необходимо для реализации метода doTask.Из возвращаемого значения видно, что этот процесс является асинхронным. Что касается механизма повтора, пользователь может указать, следует ли повторять попытку или нет.Как только TaskInfo.needRetry=true установлен (это не случайно, по умолчанию разрешено повторение), механизм повтора включается. Вы также можете установить количество повторных попыток (TaskInfo.retryTimes). По умолчанию три раза с интервалами 1 с, 2 с и 3 с соответственно. Интервал времени состоит из арифметической последовательности с допуском 1. Конечно, пользователю не будет позволено повторять бесконечно, система имеет встроенное максимальное количество повторных попыток, а максимальное количество повторных попыток встроено в 5 раз.
Почему 5 раз?
Это чувствуется, 1с, 2с, 3с, 4с, 5с, вся цепочка запросов растянута до 15с, что для клиента катастрофа! !
③ Затем поговорим о платежном канале (PayChannel). Эта часть дизайна тесно связана с конкретными платежными каналами, включая настройку параметров платежа, обработку параметров платежа, подпись/проверку и т. д.
④ Наконец, объясните параметры платежа (PayParams).
Большинство все еще может понять, я объяснил несколько ключевых свойств:
1) appId, который настроен для различения разных продуктов. На самом деле очень вероятно, что продукт подаст заявку на соответствующий платежный канал, а затем создаст приложение на платежной платформе, установит соответствующие параметры платежа, а система присвоит appId, который можно использовать для непосредственного нахождения каждого параметр платежа. . Если вы хотите быть более совершенным, вы можете различать тестовую среду и формальную среду;
2) сумма, здесь представляет значение суммы платежа, но единица суммы этой платежной системы единообразно установлена в юанях [центах];
3) метаданные, по идее, ограничения на поле метаданных нет, если есть ограничение, то длина поля 5000 символов. В этой области еще много места для воображения: он используется для заполнения обширной информации, связанной с транзакциями, и используется для проведения углубленного бизнес-анализа в продуктах системы аналитики роста. Включая многомерный анализ поведения транзакций, анализ толпы, путь конверсии продукта, персонализированные рекомендации, интеллектуальные субсидии, целевое продвижение и т. д. Посмотрите, как будет играть продакт-менеджер;
5) учетные данные, это поле очень и очень важное, которое содержит учетные данные окончательного платежного запроса клиента, которые будут возвращены клиенту в составе объекта Payment;
Дизайн поля документа MongoDB
Объясните, почему вы должны использовать MongoDB:
Лично я считаю, что если эта общая услуга будет хорошо продвигаться (даже с открытым исходным кодом), лучшим выбором будет использование реляционных баз данных, таких как MySQL, потому что полная и практичная система неизбежно потребует базы данных. стоимость стыковки для некоторых людей. Некоторые люди видят, что он не соответствует технологическому стеку команды, поэтому не рассматривают его напрямую.
Почему я все еще должен использовать MongoDB?
① В стеке технологий команды есть такая штука, в ней нет необходимости;
② Популярность MongoDB не должна быть слишком высокой, и нет необходимости использовать вещи NoSQL, и я чувствую, что уже через несколько минут выбыл из строя;
③Сохраняемая структура данных должна поддерживать характеристики динамического расширения.Мне нравится гибкость MongoDB.Сохраняемая структура данных следующая:
document_name = "Платеж"
{
"payId": "pay_Oyvrf9vP880STm1e9G5CSCm1",
"method": "yoogurt.taxi.pay",
"version": "v1.0",
"timestamp": 1473044885,
"created": 1473042835,
"paid": false,
"appId": "app_KiPGa98abDmLe9ev",
"channel": "wx",
"orderNo": "20161899798416",
"clientIp": "192.168.18.189",
"amount": 10000,
"subject": "用户充值订单(¥100.0)",
"body": "用户充值订单(¥100.0)",
"paidTime": null,
"transactionNo": "",
"metadata": {
"user_id": "170204469176",
"phone_number": "13811234567"
},
"credential": {
"appId": "wx4932b5159d18311e",
"partnerId": "1269774001",
"prepayId": "wx201609051033574da13955420883291539",
"nonceStr": "1e99d8ffdde926ed9cbdf4d2e614abad",
"timeStamp": "1473042837",
"packageValue": "Sign=WXPay",
"sign": "1CECCE6B13C956DEBA88800B3DEC4DBE"
},
"extra": {},
"statusCode": "",
"message": "",
"description": ""
}
Среди них метаданные, учетные данные, дополнительные и другие поля не имеют особо фиксированной спецификации.При использовании MySQL вам необходимо создавать избыточные поля или разделять таблицы для каждого канала, что раздражает, когда вы думаете об этом!
MySQL
Поскольку эта платежная система предназначена для поддержки нескольких приложений, многоканальности, поэтому она используется для хранения некоторых конфигураций приложений. Карта E-R освобождена непосредственно от структуры таблицы базы данных:
① pay_channel: Платежные каналы доступны для доступа
② app_settings: информация о платежном приложении
③ app_channel: платежный канал, к которому подключено приложение.
④ alipay_settings: настройки параметров Alipay
⑤ wx_settings: настройки параметров оплаты приложения WeChat.
Если вы хотите добавить каналы оплаты, вам нужно только добавить соответствующую таблицу настройки параметров оплаты.
2. Инициировать возврат
Неудивительно, что клиенты могут инициировать возврат для каждого заказа на платформе, а также они могут возвращать средства пакетами, то есть для одного и того же заказа они могут инициировать заявку на возврат несколько раз, пока общая сумма возврата не превышает общую сумму, фактически выплаченную. Схема архитектуры показана ниже:
С процессом инициирования платежного запроса много общего, поэтому я не буду описывать их по отдельности, а поясню два ключевых момента:
- Когда клиент инициирует запрос на возврат, ему необходимо передать payId, который является идентификатором платежного объекта. Это означает, что вызывающая сторона платежной системы должна поддерживать соответствующую связь между payId и orderNo и должна получить правильный payId до того, как клиент инициирует запрос на возврат;
- После предыдущего шага это пятый и шестой шаги на рисунке, запрашивающие предыдущий объект платежа из MongoDB. Сторонние каналы обычно требуют указания номера заказа на возврат при возврате. Поскольку заказ может быть возвращен несколько раз, не рекомендуется использовать номер заказа в качестве номера заказа на возврат. Номер заказа на возврат здесь генерируется и поддерживается платежной системой.
Процесс выполнения этой части аналогичен предыдущему.Клиент инициирует запрос на возврат для формирования задачи возврата (RefundTask), которая помещается в очередь задач.Потребители достают и выполняют свою соответствующую бизнес-логику.Если возврат осуществляется успешно, будет сгенерирован объект Refund и сохранен в MongoDB.
MongoDB
document_name = "Refund"
{
"payId": "pay_Oyvrf9vP880STm1e9G5CSCm1",
"method": "yoogurt.taxi.pay",
"version": "v1.0",
"timestamp": 1473044885,
"created": 1473042835,
"refundId": "refund_kmw1vrf9wSrP1e9Gkp05CSCm1",
"appId": "app_KiPGa98abDmLe9ev",
"orderNo": "20161899798416",
"clientIp": "192.168.18.189",
"amount": 10000,
"succeedTime": 1473150835,
"transactionNo": "6405996874204000684260056054",
"refundStatus": "success",
"message": "",
"metadata": {
"user_id": "170204469176",
"phone_number": "13811234567"
},
"description": ""
}
3. Получите обратный звонок
Эта функция предназначена для того, чтобы стать частью управляемого событиями типа, поэтомуwebhooksСделай вашу часть.
Поскольку содержимое обратного вызова для каждого канала отличается, эта часть дизайна будет разделена в соответствии с каналом оплаты.
Схема архитектуры выглядит следующим образом:
После того, как пользователь завершит платеж, сторонний платежный канал асинхронно уведомит продавца об успешном платеже через адрес обратного вызова, указанный при инициации платежа.
Процесс выполнения этой части аналогичен предыдущему.Параметры обратного вызова анализируются в соответствующих PayChannels для формирования события обратного вызова (Event), которое сохраняется в MongoDB, а затем создается задача обратного вызова (EventTask) и помещается в очередь задач.Потребители извлекают и выполняют свою собственную бизнес-логику, а потребителями здесь являются вышестоящие системы бизнес-сервисов.
MongoDB
имя_документа = "Событие"
{
"eventId": "evt_la06CoQAiPojSgJKe5gt3nwq",
"created": 1427555016,
"eventType": "pay.succeeded",
"data": {
"payId": "pay_Oyvrf9vP880STm1e9G5CSCm1",
"method": "yoogurt.taxi.pay",
"version": "v1.0",
"timestamp": 1473044885,
"created": 1473042835,
"paid": false,
"appId": "app_KiPGa98abDmLe9ev",
"channel": "wx",
"orderNo": "20161899798416",
"clientIp": "192.168.18.189",
"amount": 10000,
"subject": "用户充值订单(¥100.0)",
"body": "用户充值订单(¥100.0)",
"paidTime": null,
"transactionNo": "",
"statusCode": "",
"message": "",
"metadata": {
"user_id": "170204469176",
"phone_number": "13811234567"
},
"credential": {
"appId": "wx4932b5159d18311e",
"partnerId": "1269774001",
"prepayId": "wx201609051033574da13955420883291539",
"nonceStr": "1e99d8ffdde926ed9cbdf4d2e614abad",
"timeStamp": "1473042837",
"packageValue": "Sign=WXPay",
"sign": "1CECCE6B13C956DEBA88800B3DEC4DBE"
},
"extra": {
},
"description": ""
},
"retryTimes": 0
}
В частности, объясните поле данных:
Если это успешное платежное событие, вернуть соответствующий объект Payment;
Если это время успешного возврата, будет возвращен соответствующий объект Refund.
Суммировать
Некоторые читатели могут прочитать всю статью и подумать, что это не платежная система, а просто подключенный к стороннему платежному каналу, который едва ли является шлюзом платежного канала!
Если вы так считаете, я с этим согласен.
Лично я считаю эту статью относительно приземленной, в ней не так много теоретического материала, и то, что я вижу, больше относится к уровню реализации, поэтому мне просто нужно опубликовать код!
Откровенно говоря, сторонние платежные каналы подключались много раз, но систематически они не проектировались и не обобщались, как сейчас.
Я использовал его несколько разping++В области агрегированных платежей на уровне предприятия ping++ является лидером отрасли, поэтому некоторые из моих структур данных чем-то похожи на него, и ping++ также станет объектом моего подражания и сравнения в будущем.
Это время также является первым шагом в реализации моей платежной системы, и в будущем я продолжу обогащать и улучшать собственную платежную систему.
Надеюсь, это поможет вам!
БЛАГОДАРНОСТЬ!
Ежедневный обмен галантерейными товарами, предоставление ценной информации в мире Интернета, общедоступный аккаунт WeChat: jishuhui_2015