Structuring the Seller Payout Creation Process with a PSP in Trendyol International Domain

Kerim Siper
Trendyol Tech
Published in
8 min readAug 8, 2023

A Payment Service Provider (PSP) is a company or financial institution that facilitates secure and efficient electronic payment transactions for merchants and businesses. Acting as intermediaries, PSPs ensure the smooth processing of online payments between merchants, customers, and financial institutions. In our system, we’ll use a PSP to communicate sellers’ payment amounts and make payouts to them.

To pay sellers, our Seller Payment team follows a four-stage payout process:

  1. Seller Create/Update: We process events from Trendyol’s “Seller Base” team to create and update sellers in the PSP’s system. This event occurs when sellers register on the Trendyol platform, ensuring accurate data generation and maintenance.
  2. Know Your Customer (KYC): Sellers must provide mandatory information, including identification and other required details, to comply with legal requirements for receiving payments through the PSP.
  3. Payout Creation: We generate a payout item for each payment order, based on the seller’s sold items within a specific payment period. We then request the PSP to make the payment to the seller accordingly.
  4. Webhook Integration: All actions and updates performed on the PSP’s side, including changes, are transferred to Trendyol through a webhook. Our system processes these webhook events to maintain accurate and up-to-date records.

1.a Creating The Trendyol Sellers Within The PSP’s System

When a seller is onboarded and ready to sell on Trendyol, their information is sent as a Kafka Event in the seller-activated topic. As the “Seller Payment” team, we consume this event to create the seller and bank account information in the PSP’s system. We also store the returned data from the PSP in our PayoutDB.

To ensure accuracy and completeness of seller and bank account information, we establish a transactional structure. However, making API calls to the PSP can compromise the transactional state. If an error occurs before recording the data in the PayoutDB, the information will be missing from the respective tables. To prevent this, we implement a stateful approach which can be found below.

Seller Create Event Consumer Flowchart

1.b Processing Trendyol Seller Updates within the PSP’s System

When a seller updates their information on Trendyol, a Kafka Event is generated in the “seller-updated” topic by the “Seller Base” team. The event payload contains the updated seller details.

To ensure accurate updates of seller and bank account information on both the PSP and Trendyol sides, we establish a transactional structure and follow these steps:

  1. If the seller record is missing from the PayoutDB’s “sellers” and “bank_accounts” tables, we create the seller and bank account in both the PSP and PayoutDB. This aligns the data between the two systems.
  2. If the payload indicates changes requiring an update to the “seller” record in the PSP, we make the necessary updates using the “PUT /users/{user-id}” endpoint and reflect the changes in the “sellers” table.
  3. If the payload indicates changes requiring an update to the “bank_accounts” record in the PSP, we update the relevant information using the “users/{user-id}/bank-accounts/{bank-id}” endpoint and reflect the changes in the “bank_accounts” table.
Seller Updated Event Consumer Flowchart

2. Know Your Customer (KYC) Process

The PSP collects necessary KYC information through a web-view accessed by sellers in the Trendyol system. To initiate the KYC process, the KYC status is checked in the “sellers” table. If KYC is incomplete, a token is obtained through the PSP’s “POST auth/{user-id}” endpoint to open the web-view for document submission.

3.a Creating “UNPAID” Payout entities from “TRANSFERRED” Payment Orders

At Trendyol, sellers’ earnings are calculated and recorded as “Payment Orders”. These orders contain seller information, payment status, amount, and date. Initially, a payment order is created with the “UNPAID” status, indicating that the payment has not been made yet.

  {
"id": 1,
"seller_id": 100,
"created_date": "2022-07-21 09:00:00.000000",
"last_modified_date": "2022-07-21 09:00:00.000000",
"amount": 150.00,
"status_id": "UNPAID",
"payment_date": null,
"payment_type_id": null,
"description": null,
"bank_reference": null,
"currency": "EUR",
"deleted": false,
}

/* An Example Of The "UNPAID" Payment Order Entity */

When a payment order is generated, a corresponding payout entity is created to facilitate payment transfer to the PSP. The payout entity includes information from the payment order, such as seller details, payment status, amount, and date. It also includes the clientPaymentID, clientPaymentStatus, and the seller’s IBAN number associated with their Trendyol account for payment purposes.

In the table below, you can find a newly created payout record in the “UNPAID” status, indicating that the payment has not yet been transferred to the PSP.

  {
"id": 1,
"created_date": "2023-06-13 09:00:00.000000",
"last_modified_date": "2023-06-13 09:00:00.000000",
"seller_id": 100,
"status": "UNPAID",
"amount": 150.00,
"currency": "EUR",
"payment_date": null,
"client_payment_id": null,
"client_payment_status": null,
"iban": null,
}

/* An Example Of The "UNPAID" Payout Entity */

Payment orders are stored in the “paymentDB” database, while payouts are stored in the separate “payoutDB” database. This division allows for independent management of payment order creation and payout processes. When payments are ready to be made to sellers, an API call updates the payment orders from “UNPAID” to “TRANSFERRED” status in Trendyol’s payment order system.

To create the corresponding payouts, we use the Debezium CDC tool. It captures changes in the “payment_order” table of the “paymentDB” and sends them to a Kafka topic called “debezium.payment-order-cdc”. From this topic, we consume the updated payment orders that have transitioned to the “TRANSFERRED” status.

We then verify the completeness of seller information in the “sellers” and “bank_accounts” tables of the “payoutDB” database. For each payment order, we create a payout entity with an “UNPAID” status in the “payoutDB”. This process ensures the separation of payment orders and payouts, enabling efficient management and synchronization of data between the two systems.

“UNPAID” Payout Creation Process

3.b Sending “Payout” entities created for sellers to the PSP

After creating “UNPAID” payout entities in the “payoutDB”, we utilize Debezium with the “payout-connector” to capture operations in the “payouts” table. These operations are published to the “debezium.payout-cdc” topic.

From the filtered “debezium.payout-cdc” topic, we consume messages for payouts in the “UNPAID” status. We retrieve the relevant seller’s information from the “bank-accounts” and “sellers” tables, including details required for PSP payments.

Using this information, we create a payload and make a request to the PSP’s “POST /payments” endpoint to initiate the payment. Based on the PSP’s response, we update the payout item to “TRANSFERRED” if the payment is successfully initiated. If the payment fails, we mark the payout as “FAILED” and update the relevant information in the tables.

Sending Payout To PSP And Updating The Payout Status as “TRANSFERRED” Process

4. Webhooks — Completing the Payout Process with Payment Notifications from the PSP

To receive transaction events from the PSP, We have implemented the “payout-gateway” application and a webhook endpoint. These events are sent by the PSP through the webhook endpoint and logged in the “webhooks” table for record-keeping. This allows Trendyol to stay updated with PSP transactions and ensure the completion of the payout process while maintaining accurate transaction records.

Additionally, in case there is any update regarding “seller” and “bank_account” Information on the PSP side other than “payout”, these details will also be sent to our gateway application with a specific type definition unique to them.

{
"id":1,
"webhook_id":"wbh-1",
"created_date":"2023-04-19 09:00:00.000000",
"last_modified_date":"2023-04-19 09:00:00.000000",
"type":"PAYMENTS.UPDATED.STATUS.COMPLETED",
"target_entity":"PAYOUT",
"target_field":"STATUS",
"status":"COMPLETED",
"client_id":"pmt-1",
"event_created_date":"2023-04-19 08:45:00.000000"
}

/* An Example Of A Webhook Event Sent From PSP */

Webhook events with the “COMPLETED” status indicate payments made to sellers. To handle this, a “webhook-connector” captures operations on the “webhooks” table using Debezium. These operations are published to the “debezium.webhook-cdc” topic for further processing.

The “debezium.webhook-cdc” topic is filtered to consume “payment-completed” events from the PSP. This updates the corresponding payout item to “PAID” status and sets the client_payment_status as “COMPLETED”.

Next, the “debezium.payout-cdc” topic is consumed on the “paymentDB” side, filtering payout events that transitioned to “PAID” status. This updates the corresponding payment order to “PAID” status and populates the paymentDate field.

Webhook Event Consume Process By Target Entities

A Final Conclusion : What did we wanted to do and what did we achieved by designing the payout process,

The purpose of the Payout system is to facilitate smooth payment processing when a payment needs to be issued through a PSP. With the system we have designed, when a “payment_order” record is created from any other system, it enables us to initiate the payment through the PSP.

The entire payment process has been designed based on an “Event Driven” architecture instead of a “Sync” structure. In a Sync structure, if an error occurs at any point during payment processing, it could lead to a complete rollback of the entire process. However, with our Kafka Event Driven architecture, when an error occurs, the system will continue processing after the relevant problem is fixed, ensuring smoother operations.

We made efforts to minimize “Synchronous API call” requests to the PSP side. To achieve this, we implemented a webhook system and processed the notifications sent by the PSP using an event-driven architecture. With the webhook system in place, our number of requests to the PSP is reduced, ensuring that we won’t encounter any Rate-Limiting restrictions imposed by the PSP, allowing for smoother and uninterrupted operations.

Lastly, instead of collecting and sending all payments in bulk, we leveraged the singular event-driven structure to transform our “payment_orders” into “payouts” in parallel, enabling seamless processing for each individual “payment_order”. As a result, each payment order can be completed within its own process without being impacted or delayed due to any errors that might occur during the sending/payment processing of another payment order. This approach ensures a more efficient and independent handling of payment orders, leading to improved overall performance and reliability.

Thank you so much for taking the time to read! We hope you found it informative and valuable.

We’re always looking for passionate and talented individuals to join our team. Learn more and apply from the links below.

--

--