ChapaChapa Docs

Inline.js

Embed Chapa checkout directly into your website for a smoother, in-page payment experience.

Chapa Inline.js allows you to embed Chapa checkout directly into your website or application, providing a smoother, in-page payment experience instead of a full-page redirect.

Inline.js is ideal when you want:

  • A modern, in-page checkout experience
  • Faster user flow with fewer context switches
  • Better conversion compared to full-page redirects
  • Minimal frontend effort while Chapa handles payment processing securely

Important: Your secret API key must always remain on your server. Inline integrations should use a public key (if supported by your setup), or a server-generated checkout session returned from your backend.

How Inline Checkout Works

  1. Customer clicks Pay Now
  2. Your frontend triggers Inline.js
  3. Inline.js opens the checkout UI
  4. Customer completes payment
  5. Your system confirms payment via:
    • Server-side verification
    • Webhooks (recommended)

Frontend callbacks are UX signals only, not proof of payment.

Even when using Inline.js, the safest and most consistent flow is:

  1. Frontend collects customer and order info
  2. Backend initializes payment (POST /v2/payments/hosted)
  3. Frontend opens Inline checkout using returned session/URL
  4. Backend verifies payment (GET /v2/payments/{reference}/verify)
  5. Webhooks confirm final state

Step 1: Add Inline.js to Your Page

Include the Inline.js script in your HTML:

<script src="https://checkout.chapa.co/checkout.js"></script>

If your production Inline.js URL differs, replace it with the official script provided by Chapa.

Step 2: Create a Pay Button

<button id="pay-btn">Pay Now</button>

Step 3: Initialize Inline Checkout

Inline.js should never call Chapa directly with secret keys. Instead, your frontend requests a payment session from your backend.

Example Frontend Code:

<script>
  document.getElementById("pay-btn").addEventListener("click", async () => {
    const res = await fetch("/initiate-payment", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        amount: 25000,
        currency: "ETB",
        merchant_reference: "ORDER_" + Date.now(),
        customer: {
          first_name: "John",
          last_name: "Doe",
          email: "john.doe@example.com",
          phone_number: "251722927727"
        },
        meta: {
          order_id: "ORD-99887",
          source: "inline_checkout"
        }
      })
    });
    const data = await res.json();
    if (!data?.data?.checkout_url) {
      alert("Failed to initialize payment");
      return;
    }
    // Open inline checkout or redirect (depending on Inline.js behavior)
    window.location.href = data.data.checkout_url;
  });
</script>

Currency may be one of: ETB, USD, UGX, DJF. Availability depends on payment method and region.

Backend Responsibility (Required)

Your backend must:

  • Validate input (amount, currency, customer)
  • Call POST /v2/payments/hosted
  • Return only the necessary session data (checkout_url) to the frontend

Never expose CHAPA_TEST_ or CHAPA_LIVE_ keys in JavaScript.

Handling Payment Results

Inline.js integrations typically expose frontend callbacks such as:

  • onSuccess
  • onClose
  • onError

Best-practice rule: Treat frontend callbacks as UX feedback only. Never mark orders as paid based on frontend callbacks.

Payment Verification (Server-Side)

After checkout completes, your backend must verify the payment:

GET /v2/payments/{reference}/verify

Verification confirms:

  • Payment actually succeeded
  • Amount and currency are correct
  • Transaction belongs to your merchant account

Only after verification returns success should you:

  • Fulfill the order
  • Grant access
  • Ship goods

Webhooks provide the most reliable confirmation of payment state.

Common events:

  • payment.success
  • payment.failed
  • payment.cancelled
  • payment.incomplete
  • payment.auth_needed
  • payment.blocked

Best Practices:

  • Validate webhook authenticity
  • Handle duplicate events safely
  • Update internal transaction state
  • Optionally re-verify critical payments

Common Mistakes

MistakeWhy It's a Problem
Putting secret keys in JavaScriptExposes your account to unauthorized access
Trusting inline callbacks without verificationCallbacks can be spoofed or manipulated
Skipping idempotency on retriesMay cause duplicate charges or orders
Delivering value before verificationRisk of fraud and revenue loss
  • Always store merchant_reference
  • Attach meta.order_id for reconciliation
  • Use idempotency keys on retries
  • Use webhooks as the source of truth
  • Keep frontend logic lightweight

When to Use Inline.js

Use Inline.js when you want:

  • Embedded checkout experience
  • Minimal UI disruption
  • Faster user flow

Avoid Inline.js if you need:

  • Fully custom UI
  • Direct charge flows
  • Deep native mobile integration

Next Steps

On this page