Skip to main content
Mini Apps can embed payment experiences as part of a broader user journey. While the user interaction happens inside the Mini App, all payment execution and final confirmation follow the same rules defined in the Payment Integration chapter. This section explains how Mini Apps trigger payments, which invocation models are supported, and how user authorization and backend responsibilities fit together.

Core Principles

Payments inside Mini Apps follow these core principles:
  • Mini Apps never authorize or finalize payments
  • Mini Apps only trigger payment experiences
  • Merchant backends are the only components allowed to call Rebell Payment APIs
  • Final payment results are delivered asynchronously via webhooks
  • Mini App UI must always reflect backend-confirmed state

Mini App Payment Capabilities (JSAPI)

The Rebell platform exposes payment-related capabilities to Mini Apps through client-side APIs (JSAPI). These capabilities allow a Mini App to:

Initiate Payment

Start a payment flow from the UI

Open Payment UI

Display the Rebell payment confirmation screen

Resume Payment

Continue an existing payment flow

What JSAPI Payment Capabilities Do NOT Do

Payments must be created server-side via OpenAPI
They act as entry points into the Rebell payment UX, always relying on a payment that has already been created or prepared by the merchant backend.

Supported Payment Invocation Models

Mini Apps support multiple payment invocation models. The correct model depends on where the payment is created and how the Mini App triggers the payment UI. This is the most common and recommended model. Characteristics:
  • Backend remains fully in control of payment creation
  • Mini App only triggers the user-facing confirmation
  • Clear separation of responsibilities
  • Best alignment with security and compliance requirements
Suitable for:
  • Service flows
  • Bookings
  • Digital goods
  • Any user-initiated payment inside a Mini App
This model is recommended for all new integrations.

Model B: Mini App–Triggered, Backend-Completed (Advanced)

In this model, the Mini App initiates the intent to pay, but the backend completes or resumes the payment asynchronously. Flow overview:
1

Trigger Intent

Mini App triggers a payment intent
2

Backend Creates/Resumes

Merchant backend creates or resumes a payment
3

Invoke UI

Mini App invokes the payment UI using a backend-provided reference
4

Standard Flow

Payment completes through standard Rebell flows
5

Webhook Confirmation

Merchant backend receives webhook
Characteristics:
  • Used to resume existing or external payment flows
  • Requires careful backend coordination
  • Higher complexity than Model A
This model is intended for resuming or continuing an existing payment and should not be used for standard checkout flows.

End-to-End Payment Flow (Mini App Perspective)

From a Mini App perspective, a complete payment flow looks like this: Key points:
  • The Mini App never decides the payment outcome
  • The backend confirms success or failure
  • The UI reflects backend-confirmed state only

Implementation Guide

Step 1: Create Payment on Backend

When user initiates payment in Mini App:
// User taps "Pay" button
async function handlePayment(orderId) {
  my.showLoading({ content: 'Preparing payment...' });

  try {
    // Call backend to create payment
    const response = await callBackend('/orders/' + orderId + '/pay', {
      method: 'POST'
    });

    const { paymentReference, tradeNO } = response;

    // Invoke payment UI
    invokePaymentUI(tradeNO);

  } catch (error) {
    my.hideLoading();
    my.showToast({ content: 'Payment failed', type: 'fail' });
  }
}

Step 2: Invoke Payment UI (JSAPI)

function invokePaymentUI(tradeNO) {
  my.tradePay({
    tradeNO: tradeNO,
    success: (res) => {
      my.hideLoading();

      // NOTE: This is INDICATIVE only!
      if (res.resultCode === '9000') {
        // Show pending state, poll for confirmation
        showPendingState();
        pollPaymentStatus();
      } else if (res.resultCode === '6001') {
        // User cancelled
        showCancelledState();
      } else {
        // Other result - still poll backend
        pollPaymentStatus();
      }
    },
    fail: (err) => {
      my.hideLoading();
      handlePaymentError(err);
    }
  });
}

JSAPI Result Codes

CodeMeaningAction
9000Payment appears successfulPoll backend for confirmation
6001User cancelledAllow retry
6002Network errorRetry or poll status
4000Order paid or closedCheck backend status
OtherUnknownPoll backend status
Never trust JSAPI result codes as final. Always confirm with backend status.

Step 3: Handle Webhook (Backend)

app.post('/webhooks/payment', async (req, res) => {
  // 1. Verify signature
  const isValid = verifyWebhookSignature(req.headers, req.body);
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // 2. Extract payment info
  const { paymentId, paymentRequestId, result } = req.body;

  // 3. Find and update order (idempotent)
  const order = await findOrderByPaymentId(paymentId);
  if (!order) {
    console.error('Order not found for payment:', paymentId);
    return res.status(200).send('OK'); // Still acknowledge
  }

  // 4. Update only if not already final
  if (order.paymentStatus !== 'SUCCESS' && order.paymentStatus !== 'FAILED') {
    await updateOrder(order.id, {
      paymentStatus: result.resultStatus === 'S' ? 'SUCCESS' : 'FAILED',
      paymentResult: result
    });
  }

  // 5. Acknowledge receipt
  res.status(200).send('OK');
});

Step 4: Poll for Status (Mini App)

async function pollPaymentStatus() {
  const maxAttempts = 10;
  const intervalMs = 3000;

  for (let i = 0; i < maxAttempts; i++) {
    try {
      const status = await callBackend('/orders/' + orderId + '/status');

      if (status.paymentStatus === 'SUCCESS') {
        showSuccessState();
        return;
      } else if (status.paymentStatus === 'FAILED') {
        showFailedState();
        return;
      }

      // Still pending, wait and retry
      await sleep(intervalMs);

    } catch (error) {
      console.error('Status check failed:', error);
    }
  }

  // Max attempts reached
  showUnknownState();
}

User Authorization & Identity in Payment Flows

Mini Apps operate within a user session provided by the Rebell SuperApp, but they must not assume unrestricted access to user information. Before accessing certain user data or performing sensitive actions, a Mini App must:
1

Request Authorization

Explicitly request user authorization via JSAPI
2

Inform User

Clearly inform the user about the requested scope
3

Respect Consent

Respect user consent decisions
User authorization in Mini Apps:
PropertyDescription
Consent-basedUser must explicitly approve
ScopedLimited to requested permissions
RevocableUser can revoke access
No credentials exposedNo sensitive identifiers shared
For payment-related flows, authorization may be required to:
  • Associate the payment with the correct user
  • Personalize the user experience
  • Fulfill regulatory or compliance requirements
Mini Apps must always treat user identity information as contextual, not as a source of truth. The merchant backend remains responsible for validating and persisting user-related data.

UX and Error Handling Guidelines

Mini App payment UX must be explicit and transparent.
  • Clearly present payment details before initiation
  • Show amount, description, and merchant name
  • Provide clear “Pay” call-to-action
  • Inform users when they are entering a payment step
  • Display loading/pending states while awaiting confirmation
  • Don’t close payment UI prematurely
  • Handle user cancellation gracefully
  • Map technical failures to user-friendly messages
  • Show confirmation only after backend confirms

Common UI States

Show loading indicator while preparing payment
my.showLoading({ content: 'Preparing payment...' });
Mini Apps must not display “payment successful” until the backend confirms the result.

Error Message Mapping

Technical ErrorUser Message
Network timeout”Connection issue. Please try again.”
User cancelled”Payment cancelled. You can try again when ready.”
Insufficient funds”Payment could not be completed. Please check your balance.”
Invalid payment”Something went wrong. Please try again.”
Backend error”We’re having trouble. Please try again later.”

Security Rules

When implementing payments inside Mini Apps:
Never:
  • Embed API keys or secrets in Mini App code
  • Sign payment requests client-side
  • Trust client-side payment status
  • Display success without backend confirmation
Always:
  • Rely on backend webhook confirmation
  • Enforce idempotency on backend payment creation
  • Validate user session before creating payments
  • Log all payment attempts and results

Security Checklist

  • No secrets in Mini App code
  • No direct Rebell API calls
  • Payment status from backend only
  • Session-based authentication
  • Webhook signature verification
  • Session validation before payment
  • Idempotent payment creation
  • Order ownership validation
  • Clear payment amount display
  • No misleading success messages
  • Proper error handling
  • Cancellation support

Testing Payments

Sandbox Testing Checklist

  • Payment created successfully
  • Payment UI opens correctly
  • User can complete payment
  • Webhook received
  • UI shows confirmed success
  • User cancels payment
  • Mini App handles gracefully
  • Can retry payment
  • No duplicate payments created
  • Network timeout handling
  • Backend error handling
  • Invalid session handling
  • Expired payment handling
  • Double-tap prevention
  • App backgrounding during payment
  • Webhook delay handling
  • Concurrent payment attempts

Relationship with Payment Integration Chapter

This section explains how Mini Apps trigger and embed payments. For full details on:
  • Payment creation
  • Payment lifecycle
  • Error handling
  • Inquiry API
  • Webhooks
Refer to the Payment Integration chapter.

Next Steps