Skip to content

Payment module overview

Introduction

IxDF-web app has [a few payment flows](Payment flows) that lead to a page with a CheckoutForm component.

Recurring payments through durable tokenization

Before moving into payments, it is important to understand that there are two forms of credit card tokenization: durable (or card-based) tokens and transaction-based tokens. The IxDF only uses durable tokens, but it’s good to understand what transaction-based tokens are and when they might be used.

First, let’s start by defining what we mean by tokenization, or the process of creating tokens. In the electronic payments world, tokenization is the process of replacing a real credit card number (also known as the Primary Account Number, or PAN) with a surrogate value called a token. In most cases, a merchant passes the real credit card number – in encrypted form – to a third-party token vault manager, which stores the real PAN in a highly secure database called a "vault" (in our case, the vault managers are our payment gateways, namely PayPal and Quickpay and perhaps Checkout.com). At the same time, the vault manager generates a non-sensitive random value called a "token" that is associated with that specific PAN or transaction via an index. The vault manager sends the token to the merchant for storage or future use.

Notice that we said that a token can be associated with a specific PAN (credit card) or with a specific transaction. This is the difference between a durable token and a transaction-based token.

A durable token is one that is assigned to a specific credit card for a life-long association. Every time the cardholder wants to make a payment to a certain merchant using that card, the exact same token is invoked time after time. Merchants that have online retail or wholesale stores appreciate this durable relationship between the card number and the token because they can offer their customers the ability to save cards to their account, which makes the checkout process a breeze, reduces the likelihood of the customer abandoning the shopping cart and increases the chances that a customer will come back to make a future purchase. Merchants that accept credit cards for invoice payments or phone orders appreciate that they can quickly process a transaction using the "card-on-file" upon customer instruction, allowing them to get paid faster and with minimal customer action, such as mailing a check or calling in with the correct credit card number. Durable tokens enable long-term storage as well as various back-end business processes without exposing sensitive data.

A transaction-based token is generated and used for one specific transaction. If a customer initiates multiple transactions with a merchant using the same credit card each time, there is a new token issued for each transaction. This works just fine for small merchants that don’t typically need to associate the token number back to a specific customer again and again. For example, consider a merchant selling T-shirts in a booth at a concert venue. The merchant doesn’t really care about tracking the customers' historical buying patterns or initiating multiple sales over time to the same customer. At the same time, the merchant doesn’t want to store real PANs in its sales system. In this case, transaction-based tokens fit the need nicely.

About fraud prevention through AVS and CVV

AVS and CVV: We can use credit card verification to protect ourselves against fraudulent transactions. However, we are NOT interested in doing this since it will probably also decline some valid credit cards. If someone uses a stolen credit card to get into our platform, it does not have any consequences for us. We simply delete the account when we find out.

IxDF Payment System API

Transactions

All interactions with third party payment services are represented by instances of the \App\Modules\Payment\Models\Transaction model.

Each Transaction object corresponds to an external transaction executed on a given external payment service provider. These external transactions are called native transactions. Whenever this document uses the term native transaction it refers to an operation (charge, refund or payout) that is performed on the third party payment service provider (PayPal, Stripe, Authorize.net, Checkout.com and so on).

Transactions can be synchronous or asynchronous. Synchronous transactions are carried out immediately and their results are known immediately, while asynchronous transactions are started by application code and completed at some point in the future. Whether a transaction is synchronous or asynchronous usually depends on the payment gateway used.

If a transaction is asynchronous, application code starts the transaction and at some point - when the result of the transaction is known and the transaction transitions to the "succeeded" state - an associated order is then fulfilled.

A transaction may also use hosted checkout pages. Hosted checkout pages means web pages hosted elsewhere, outside IxDF’s system. In this case, in order to complete the payment, the user is redirected to the checkout page (e.g. a checkout flow on PayPal’s website). Transactions made through hosted checkout pages are always asynchronous transactions, but the inverse is not true, i.e., not all asynchronous transactions are hosted checkout transactions. There may be payment gateways that do not return the transaction result immediately but still provides an API that allows the application to perform the checkout itself.

Each transaction has a status that tells what the transaction’s state is as well as what happened to the transaction. The possible statuses are:

  • pending: Incomplete transaction (for async flows).
  • requires_capture: Transaction requires a manual capture.
  • succeeded: Succeeded completed transaction.
  • canceled: Transaction canceled by a customer or our staff (it’s possible to cancel a "pending" transaction only).
  • capture authorization expired: Capture authorization expired, it is no longer possible to capture transaction amount.
  • declined: A gateway processed the transaction but declined it for some reason (insufficient_funds, stolen_card, etc).
  • runtime error: A runtime error has occurred (network, configuration error, server error on gateway). It means a gateway has not charged a payment method, and it's safe to try again.

Each transaction is also tied to a bank account, i.e., an actual funding source where money goes to. The bank account is an arbitrary string identification used for financial reports and accounting. It is up to the payment gateway to provide this information when creating a transaction and it is up to application code to copy it to the invoice on a successful transaction.

If an error has occurred, the transaction contains the error code and error message, if any, reported by the corresponding layer.

On-Session vs. Off-Session Transactions

Transactions can be classified as either on-session or off-session. An on-session transaction occurs when a member (or different type of user) adds items to a cart and completes all steps of the checkout flow. In contrast, an off-session transaction takes place without the user’s direct interaction, typically in the background using a stored payment method. Off-session transactions are most commonly used for automatic membership renewals.

Manual Capture

Transactions may have an intermediary "requires capture" state, which is useful when IxDF wants to provide a service without charging for it straight away. Support for manual payment capture has been added to be able to provide new members with a trial membership. An amount is held on their payment method when they sign up and captured at a later time, at the end of the trial period.

All transactions that require a manual capture have two timestamps:

  • requires_capture_before - indicates what is the deadline to capture transaction amount before the transaction that requires capture becomes expired
  • captured_at - indicates when the transaction amount was successfully captured

Transactions that have an expired capture authorization are transitioned to "capture authorization expired" status by listening to the charge.expired Stripe webhook event.

Payments and Payment Methods Types

A payment method is how one is going to pay for a transaction. Example of payment methods are:

  • Credit Card
  • Debit Card
  • PayPal Account
  • BitCoin
  • Personal Check
  • Wire Transfer

Payment methods are represented by App\Modules\Payment\Entities\PaymentMethodType enum. Payment methods have an ID for internal use. Each payment method also has a corresponding Blade component that defines its human friendly name along with other payment method’s properties and behaviors.

Examples:

  • App\Modules\Payment\View\PaymentMethods\Stripe\CreditCardPaymentMethod
  • App\Modules\Payment\View\PaymentMethods\PayPal\PayPalPaymentMethod

In order to make a payment it is necessary to choose a payment method, a payment gateway and provide any additional payment info the payment gateway may need to process that payment method. The amount of data necessary depends both on the payment method and on the payment gateway and must be documented as part of the payment gateway documentation. It is up to application code to provide a suitable UI to collect the necessary info.

Once a payment is completed, some gateways allow to store a payment for future use.

Stored payment methods may be attached to members or companies and saved in order to be reused later, either for automatic, recurring billing, or to avoid reentering the payment info again.

The API is straightforward, besides the common methods to create, update and delete rows, there should be methods to get and update the relationships to member and companies, and to get the preferred payment method instance of a Member/Team.

Payment Gateways

Payment gateways are classes that provide financial transactions (i.e. charges and refunds) using an external third party service provider. They may also implement additional, gateway specific, operations to interact with the external service’s native transactions for debugging and auditing purposes. These additional operations, if any, must be labeled as @internal and must not be used outside of those contexts.

Payment gateways support at least one payment method, but may support more. Appropriate APIs are provided so that application code can know what are the payment methods available in order to construct a suitable payment method selection UI. For a better understanding of the logic behind payment method selection please refer to App\Modules\Payment\View\CheckoutForm component.

Payment gateways must implement the PaymentGateway contract, which defines basic methods all payment gateways must implement.

Currencies

What currencies are supported on the application-side?

GatewayCurrencies
Stripe USAAll currencies are supported, no restrictions
Stripe DenmarkAll currencies are supported, no restrictions
PayPal USADefined in a config file
PayPal DenmarkDefined in a config file
Amazon PayUSD

What currencies are supported on the gateway-side?

GatewayCurrencies
StripeSee the list
PayPalSee the list
Amazon PayThe ledger currency is USD. Amazon Pay offers a multi-currency capability, but all funds get converted to USD before being credited to the account balance.

Currency conversion to USD

Members and paying guests can purchase products in various currencies. To be able to sum, compare and analyze the amounts, all amounts must be converted to USD. The conversion is done using the exchange rate at the time the order is paid (based on the Order’s paid_at timestamp) through the CurrencyExchange service. Since the exchange rate is retrieved from an external service, the conversion is handled asynchronously via the following queued jobs:

  • FilloutInvoiceTotalUsd - converts the total amount of the invoice to USD
  • FilloutOrderLineTotalUsd - converts the total amount of the order line to USD

Membership Prices

Membership prices are not uniform for all members. Both the amount and the currency depends on the country the member is from. In order to make it easy to define a uniform price for several countries, we define price schemes. A price scheme is just a way of grouping a unique combination of currency and amounts and give it a name so that they can be managed as a whole.

Then we have the actual prices plus some helper methods to make it easy calculating downgrades/upgrades amounts:

Orders

Orders represent everything there is to know about what a Payer/owner intends to purchase.

  • invoice_number starts with a PF- (pro-forma) prefix
  • paid_at timestamp is null
  • paid_transaction_id is null

Paid sale invoices remain pretty much the same, but with the following notable changes:

  • final invoice_number is assigned to the invoice
  • paid_at timestamp is set
  • paid_transaction_id is added to hold the ID of the corresponding succeeded transaction

It is common for the checkout flow to be initiated by a payer such as AlmostMember, but close to the end of the flow, the ownership of the invoice needs to be delegated to the final owner, for example a Member or a Team. Entities capable of delegating invoice ownership implement the DelegatesInvoiceOwnership contract.

Stored Payment Methods

Members or Team Managers can manage stored payment methods via the Billing tab on the private profile. From there, they can select a default payment method, add new payment methods, or remove existing ones.

We remind members to always maintain at least one stored payment method on the platform. Members without any stored payment methods will see a warning message:

Please add a payment method. All members need to have a saved payment method.

Occasionally, members may attempt to add the same payment method multiple times. In such cases, the system will replace stored payment methods that share the same fingerprint and the same expiration date as the newly added card, ensuring no duplicate cards are stored for the same member.

Expired Stored Payment Methods

The system automatically deletes expired payment methods in the background. For more details, refer to the DeleteExpiredPaymentMethods scheduled command. If this process leaves a member without any stored payment methods, they are notified via email to add a new one. Furthermore, when a default payment method is removed due to expiration, the system tries to find another valid, non-expired payment method and set it as the default.

Further Improvements

  • Physical/digital products

Events

Payment orders:

  • \App\Modules\Payment\Events\OrderFulfilled
  • \App\Modules\Payment\Events\OrderPaid

Credit Notes and Refunds:

  • \App\Modules\Payment\Events\CreditNoteIssuedWithCompletedRefund
  • \App\Modules\Payment\Events\CreditNoteIssuedWithPendingRefund
  • \App\Modules\Payment\Events\PendingRefundTransactionCompleted