How Payment Gateway Works

When a customer clicks “Pay Now” on your website, a surprisingly complex series of events happens behind the scenes — all within a few seconds. Understanding this flow matters whether you’re a developer integrating a payment gateway, a business owner evaluating options, or simply someone curious about how money moves on the internet.

In this post, we’ll walk through the complete payment flow using Midtrans, one of the most widely used payment gateways in Southeast Asia, as our reference point.

What Is a Payment Gateway?

A payment gateway is a service that acts as the bridge between your application (the merchant side) and the financial system (banks, e-wallets, card networks). It handles the secure transmission of payment data, communicates with the relevant financial institutions, and returns the result — success, failure, or pending — back to both the merchant and the customer.

Midtrans supports multiple integration styles: Snap (a ready-made payment UI), Core API (full custom control), and Payment Link (no-code). The underlying payment flow, however, is fundamentally the same across all of them.

The Key Players

Before diving into the flow, it helps to know who’s involved in every transaction:

  • Customer: the person paying
  • Merchant’s frontend: your website or app
  • Merchant’s backend server: your own server that talks to Midtrans
  • Midtrans server: the payment gateway itself
  • Bank / E-wallet / Card network: the institution that holds the customer’s money
  • Issuing bank: the bank that issued the customer’s card or account
  • Acquiring bank: the bank that receives funds on behalf of the merchant

Step 1: Customer Places an Order

The flow begins when a customer adds items to a cart and clicks checkout. At this point, your frontend collects order details (item names, quantities, total price, customer information) and sends them to your backend server.

Nothing has touched Midtrans yet. Your server is simply aware that a payment request needs to be created.

Step 2: Your Server Requests a Transaction Token from Midtrans

This is the first communication between your system and Midtrans. Your backend sends an API request to the Midtrans server containing the order information — order ID, gross amount, item details, and customer data. This request is authenticated using your Server Key, which is kept secret on your backend and never exposed to the frontend.

Midtrans receives this request, validates it, creates a transaction record on their side, and responds with a transaction token (in Snap integration) or a payment URL depending on the integration type you’re using.

A typical request payload to Midtrans looks like this:

POST https://app.midtrans.com/snap/v1/transactions
Authorization: Basic [Base64 encoded Server Key]

{
  "transaction_details": {
    "order_id": "ORDER-001",
    "gross_amount": 150000
  },
  "customer_details": {
    "first_name": "Budi",
    "email": "budi@example.com"
  }
}

Midtrans return something like:

{
  "token": "66e4fa55-fdac-4ef9-91a5-f82f8d123456",
  "redirect_url": "https://app.midtrans.com/snap/v2/vtweb/66e4fa55-fdac-4ef9-91a5-f82f8d123456"
}

Step 3: Your Server Forwards the Token to the Frontend

Your backend sends the token or redirect URL back to your frontend. The frontend then uses this token to either open the Midtrans Snap payment popup (embedded in your page) or redirect the customer to Midtrans’ hosted payment page.

This is an important security boundary. Your Server Key never leaves your backend. The frontend only receives a short-lived token that is scoped to this specific transaction.


Step 4: Customer Sees the Payment Interface and Chooses a Method

The Midtrans payment UI loads and presents the customer with available payment methods — credit/debit card, bank transfer, GoPay, ShopeePay, QRIS, over-the-counter payment, and more. The customer selects their preferred method and fills in the required details (card number, OTP, etc.).

At this point, the payment page is hosted and secured by Midtrans, not your website. This is intentional — it means sensitive card data never touches your server, which reduces your compliance burden (PCI DSS scope).

Step 5: Customer Submits Payment: The Charge Request

Once the customer clicks “Pay,” the Midtrans frontend SDK sends a charge request directly to the Midtrans backend. For card payments, this includes triggering a tokenization step first — the raw card number is converted into a safe token using Midtrans’ Client Key, and only that token travels to the backend.

Midtrans then does the heavy lifting: it communicates with the relevant card network (Visa, Mastercard) or e-wallet provider, routes the request to the appropriate issuing bank, and waits for an authorization response.

Step 6: Bank Authorization (Authorize and Capture)

On the bank side, two processes happen in rapid succession:

  1. Authorize: The issuing bank checks whether the customer’s account has sufficient funds or credit to cover the transaction. It also validates card details and may trigger fraud checks.
  2. Capture: If authorization succeeds, the funds are “booked” or reserved from the customer’s account. The money doesn’t move yet and it’s just held.

For card transactions using 3D Secure (3DS), the customer is redirected to their bank’s authentication page and asked to enter a one-time password (OTP) sent via SMS. This is an extra layer of verification coordinated between Midtrans, the card network, and the issuing bank. The entire authorize and capture process typically completes in milliseconds, though the customer may experience a brief wait during OTP verification.

Step 7: Midtrans Returns the Transaction Result to the Customer

Once the bank responds, Midtrans sends a charge response back to the payment interface. The customer sees an immediate result: success, failure, or pending.

A successful charge response looks like this:

{
  "status_code": "200",
  "transaction_id": "ca297170-be4c-45ed-9dc9-be5ba99d30ee",
  "order_id": "ORDER-001",
  "payment_type": "credit_card",
  "transaction_status": "capture",
  "fraud_status": "accept",
  "gross_amount": "150000.00"
}

The status capture means the payment was authorized and funds are reserved. The customer is then redirected to your finish URL (a thank-you or order confirmation page on your website).

Step 8: Midtrans Sends a Webhook Notification to Your Server

This is one of the most critical steps and one that developers sometimes overlook. After the transaction completes, Midtrans sends an HTTP POST notification (webhook) to a URL you configure.

This is how your server learns the real, authoritative outcome of the payment. You should never trust the frontend redirect alone to confirm payment success, because a customer could theoretically manipulate the redirect URL. The webhook from Midtrans is the reliable source of truth.

The webhook payload contains the transaction status, order ID, payment type, and fraud detection result. Your server should:

  1. Receive the POST request from Midtrans
  2. Verify its authenticity (using a signature key check)
  3. Look up the order in your database using the order_id
  4. Update the order status based on transaction_status
  5. Trigger downstream actions and send a confirmation email, release the product, update inventory, etc.

A webhook payload example:

{
  "order_id": "ORDER-001",
  "transaction_status": "settlement",
  "payment_type": "credit_card",
  "fraud_status": "accept",
  "gross_amount": "150000.00",
  "status_message": "Midtrans payment notification"
}

Step 9: Status Settlement: When Money Actually Moves

Settlement is the final step. This is when the financial system formally transfers money from the customer’s issuing bank to Midtrans’ acquiring bank, and eventually to the merchant’s account.

Settlement typically happens at end-of-day (cutoff time varies by payment method). Before settlement is triggered, a merchant can still cancel or void a capture transaction. Once settlement is locked, cancellation is no longer possible and only a refund can be issued after the fact.

Two API Keys, Two Purposes

Midtrans uses two distinct API keys and understanding why matters:

  • Client Key is used on the frontend. It is safe to expose in browser code and only be used for tokenizing card data.
  • Server Key is used on the backend only. Never expose this in your frontend or public repository because it is Used for creating transactions and checking status.

This separation ensures that even if your frontend code is inspected, attackers cannot create fraudulent transactions on your behalf.

What About Payment Links?

Midtrans also offers a Payment Link flow, where instead of embedding a payment UI, your backend calls the Midtrans API to generate a shareable URL. You then send this link to your customer via WhatsApp, SMS, or email. The customer clicks it, completes payment on Midtrans’ hosted page, and your webhook receives the result exactly as described above.

This is useful for offline orders, invoice-based billing, or situations where the customer isn’t completing checkout on your website in real time.

Book a Free Consultation

Haven’t found the right solution yet?
Tell us about your needs, and let’s discuss the best solution together.