Integration Guides

Hosted Checkout

Integrate NjiaPay's PCI-compliant hosted checkout page

The hosted checkout is NjiaPay's secure, PCI-compliant payment page that handles all the complexity of collecting payments. It supports multiple payment methods, 3D Secure authentication, and provides a seamless customer experience.

Why Use Hosted Checkout?

PCI Compliance Made Easy

  • No card data touches your servers
  • NjiaPay handles all PCI requirements
  • Automatic security updates

Multiple Payment Methods

  • Cards (Visa, Mastercard, Amex, etc.)
  • Bank transfers
  • Mobile money
  • Digital wallets (PayPal, Apple Pay, etc.)

Built-in Features

  • 3D Secure authentication
  • Real-time validation
  • Multi-step flows
  • Mobile-responsive design
  • Multi-language support

Reduced Development Time

  • No payment form to build
  • No validation logic needed
  • Works across all devices

Integration Flow

mermaid
sequenceDiagram
    participant Customer
    participant YourServer as Your Server
    participant NjiaPay as NjiaPay API
    participant Checkout as Hosted Checkout
    participant PSP as Payment Provider

    Customer->>YourServer: 1. Initiates checkout
    YourServer->>NjiaPay: 2. POST /api/intents
    NjiaPay-->>YourServer: 3. Returns redirect_url
    YourServer-->>Customer: 4. Redirect to checkout
    Customer->>Checkout: 5. Selects payment method
    Checkout->>PSP: 6. Processes payment
    PSP-->>Checkout: 7. Payment result
    Checkout-->>NjiaPay: 8. Updates intent status
    NjiaPay-->>YourServer: 9. Sends webhook
    Checkout-->>Customer: 10. Redirects to return_url

Step-by-Step Implementation

Step 1: Create a Payment Intent

When your customer is ready to pay, create a payment intent from your server:

curl -X POST https://app.infinic.com/api/intents \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "currency": "ZAR",
    "reference_id": "order_12345",
    "purchaser_id": "customer_abc",
    "return_url": "https://yoursite.com/payment/callback",
    "allow_remember_credentials": false,
    "purchaser_info": {
      "email": "customer@example.com",
      "phone": "+27123456789",
      "country": "ZA"
    }
  }'

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "redirect_url": "https://app.infinic.com/checkout?intent=550e8400-e29b-41d4-a716-446655440000&token=eyJ0eXAi..."
}

Step 2: Redirect Customer to Checkout

Take the redirect_url and redirect your customer to it. You can use:

HTTP 302/307 Redirect (Server-side):

app.post("/checkout", async (req, res) => {
  const { redirect_url } = await createPaymentIntent(req.body);
  res.redirect(redirect_url);
});

JavaScript Redirect (Client-side):

example.js
// After creating intent on your server
window.location.href = redirect_url;

HTML Link (Client-side):

<a href="{{ redirect_url }}" class="btn btn-primary"> Pay Now </a>

Step 3: Customer Completes Payment

On the hosted checkout page, the customer will:

  1. See available payment methods
  2. Select their preferred method
  3. Enter payment details (securely handled by NjiaPay)
  4. Complete any additional authentication (3D Secure, OTP, etc.)
  5. See payment result

The checkout page handles all validation, error messages, and user experience.

Step 4: Customer Returns to Your Site

After payment (success or failure), the customer is redirected to your return_url with query parameters:

https://yoursite.com/payment/callback?intent_id=550e8400-e29b-41d4-a716-446655440000&status=success
Important: Do NOT rely solely on the return URL parameters to confirm payment! Users can manipulate URLs. Always verify payment status via webhook or API.

Use webhooks to receive real-time payment updates:

example.js
app.post("/webhook", async (req, res) => {
  const event = req.body;

  if (event.type === "StatusChange") {
    const { intent_id, status, reference_id } = event.content;

    if (status === "success") {
      await fulfillOrder(reference_id);
      await sendConfirmationEmail(reference_id);
    } else if (status === "failed") {
      await logFailedPayment(reference_id);
    }
  }

  res.status(200).send("OK");
});

See Webhooks Guide for complete implementation.

Customization Options

Pre-filling Customer Information

Use purchaser_info to pre-fill customer details:

{
  "purchaser_info": {
    "email": "customer@example.com",
    "phone": "+27123456789",
    "country": "ZA",
    "locale": "en-ZA"
  }
}

This improves user experience by reducing data entry and helps improve the payment success rate.

Filtering Payment Methods

Limit which payment methods are shown:

{
  "payment_method_filter": {
    "filter_type": "allow",
    "payment_methods": ["card", "paypal"]
  }
}

Or block specific methods:

{
  "payment_method_filter": {
    "filter_type": "block",
    "payment_methods": ["bancontact"]
  }
}

Merchant Branding

Configure your merchant styling in the portal:

  • Logo
  • Brand colors
  • Company name

This branding appears on the checkout page.

Important Parameters

return_url Requirements

Your return_url must be:

  • HTTPS (not HTTP) for security
  • Publicly accessible (not localhost for production)
  • Under your control (your domain)
  • Able to handle query parameters

Example valid URLs:

✅ https://yoursite.com/payment/callback
✅ https://shop.example.com/checkout/return
✅ https://app.yourdomain.io/payments/complete

Example invalid URLs:

❌ http://yoursite.com/callback (not HTTPS)
❌ http://localhost:3000/callback (localhost not allowed)
❌ https://google.com (not your domain)

allow_remember_credentials

Set to true if you want to enable:

  • One-click payments (customer can save card)
  • Future recurring payments (MIT)

Set to false for one-time payments only.

If you plan to use auto-payments, you must set allow_remember_credentials: true AND request_unscheduled_mit: true.

Testing the Flow

Test in sandbox with MockPSP:

  1. Create a payment intent
  2. Open the redirect_url in a browser
  3. Select "Card" payment method
  4. Enter any card details
  5. For successful payment: Use any card holder name
  6. For declined: Set holder name to "DECLINED"
  7. For error: Set holder name to "ERROR"

See Testing Guide for more scenarios.

Common Issues

Checkout Page Not Loading

Symptom: Redirect URL results in error or blank page

Solutions:

  • Verify the redirect_url is complete and unmodified
  • Ensure intent_id and token are in the URL

Customer Can't Complete Payment

Symptom: Payment gets stuck or fails unexpectedly

Solutions:

  • Verify payment method is enabled
  • Ensure currency is supported by PSP
  • Check customer's card/account has sufficient funds

Return URL Not Working

Symptom: Customer doesn't get redirected back

Solutions:

  • Verify return_url is HTTPS and publicly accessible
  • Check for typos in the URL
  • Ensure your server is handling the redirect properly
  • Test the URL directly in a browser

Security Best Practices

✅ DO

  • Always verify payment status via API or webhook
  • Use HTTPS for your return_url
  • Store API keys securely (environment variables)
  • Validate intent_id belongs to your merchant
  • Log all payment attempts for audit

❌ DON'T

  • Trust return_url query parameters alone
  • Expose API keys in client-side code
  • Hard-code payment amounts in client code
  • Skip webhook verification
  • Use HTTP for return_url

Create Intent API

Complete API reference

Webhooks

Set up real-time notifications

Payment Intents

Understand payment intents

Testing Guide

Test your integration