This page walks through all payment interactions within BillingServices. This page avoids the details of the Billing flows that encompass these interactions to focus on payments.
Saved payment methods are retrieved by the DefaultWalletService using a SavedPaymentMethodProvider.
The payments are retrieved using a list of userIds, and the response is expected to contain all saved payment methods for the users, as restricted by the provided context (ContextInfo).
To avoid calls with heavy payloads, we request payment methods through a series of requests in quantities capped by a configuration property.
Following their retrieval, the flow proceeds to sort the list of payment methods for each user.
The default implementation is pass-through, meaning no sorting is done on the list. However, if a custom sorting logic needs to be implemented, DefaultWalletService#sortWalletPaymentAccounts is the perfect method to override.
Subsequently, the method checks for a special case, as described in Subscription’s Preferred Payment Account, for Subscriptions that explicitly specify a preferred payment method.
If a Subscription has a preferredPaymentAccountId set by the user or the system, that value is meant to override the ordering that PaymentTransactionServices (PTS) or BillingServices conclude with.
The field’s value is transferred to its BillingEventPayment’s field, preferredPaymentAccountId.
If that field is present on the BillingEventPayment, the DefaultWalletService finds the payment in question in the already-ordered list at fetches it to the beginning of the list, thereby overriding its ordering by PTS or Billing.
The transactionReferenceId is a field holding a unique value for the gateway to recognize duplicate transactions.
In this service’s communication with PTS, this field’s value is transferred in requestId fields.
It is crucial for this field to be unique for each billing request, each payment account, each transaction, and each attempt to execute the transaction. NOTE: When attempting to resolve a payment with an unknown result by retrying it (if the payment gateway allows), this unique value should be kept to ensure the gateway recognizes the transaction if it has previously ran it.
transactionReferenceIdThis field’s value is a concatenation of the BillingProcessingRequest’s id (which is a ULID) and the ULID id of each PaymentAccountProcessingRequest.
There is a caveat: if a PaymentAccountProcessingRequest does not contain a PaymentAccount, the payment request’s index in BillingProcessingRequest’s accounts list is used in its place.
This concatenation is then hashed using MD5 to prevent potential size overflows in external systems, and inserted into the PaymentAccountProcessingRequest.
When the PaymentTransactionDetail is created for this attempt, the transactionReferenceId is set as its id field as well as its transactionReferenceId field.
Before a payment transaction attempt can be sent to be executed by the PaymentTransactionExecutionProvider, a DTO needs to be built to convey the necessary information.
The two DTOs for our AuthAndCapture and Refund events - SavedPaymentMethodTransactionExecutionRequest and TransactionExecutionRequest respectively - contain the requestId field that is populated with the transactionReferenceId field from the PaymentTransactionDetail.
They also transfer a myriad of other information from the BillingProcessingRequest necessary to execute the transaction correctly.
The PaymentTransactionExecutionProvider is used to communicate with the payment provider / gateway.
The default implementation, ExternalPaymentTransactionExecutionProvider, communicates with PTS' SavedPaymentMethodTransactionExecutionEndpoint for CHARGE-type transactions and with TransactionExecutionEndpoint for REFUND transactions.
Results of the transaction are first recorded onto the PaymentAccountProcessingRequest.
Of note are the values that are added to the transactionAdditionalFields map:
The PTS-specific field PTS_PAYMENT_ID should be populated on the response DTO if a payment was successful.
The PTS-specific field PTS_PAYMENT_VERSION should be populated on the response DTO if a payment was successful.
Additional attributes from TransactionExecutionDetails of the response DTO are always propagated to the payment request. They may be used by payment gateways to offer additional information.
The PaymentTransactionDetail is then updated with the results of the transaction
|
Note
|
PTS records results for transactions it executes. On the BillingServices side, we also record transaction results we receive from PTS to alleviate reporting and debugging difficulties. |
PaymentTransactionServices (PTS) has logic to archive payment methods if their initial Authorize or AuthAndCapture transaction failed.
For Billing requests, we turn that logic off by setting CreatePaymentRequest#shouldArchivePaymentDueToFailedInitialTransaction to false.
Some gateways allow to modify the payment method e.g. when a card is past its expiration date but the customer has renewed it with their bank.
A common customization is to use another Payment Gateway. With Billing, a complete adoption of another gateway is two-phased:
Choose one of the other payment gateway modules or implement a new one. Learn more by visiting Shared Concepts: Payment Gateway Common.
Configure any special behavior the Billing service should have in mind when using the new gateway. Learn more in the Common Customizations page.