Introduction

Boxo Payments is an in-app payment functionality that enables host app users to complete payments for orders within the miniapp.

Boxo Payments flow

Here is a diagram illustrating the payment process for a miniapp order using the host app’s payment system. AuthDiagram
  1. The user navigates to the payment page in the miniapp and initiates the creation of an order.
  2. The miniapp server creates the order and sends a createOrderPayment request to the Boxo Platform.
  3. The Boxo Platform initiates the order payment process on the host app server.
  4. The host app server returns an orderPaymentID to the Boxo Platform.
  5. The Boxo Platform then forwards the orderPaymentID to the miniapp server.
  6. The miniapp server passes the orderPaymentID to the miniapp.
  7. The miniapp forwards the orderPaymentID to the Boxo JS SDK’s pay method.
  8. The Boxo Native SDK captures the Boxo JS SDK’s pay event (with the orderPaymentID) and displays a payment confirmation page to the user.
  9. The user confirms the payment.
  10. The host app processes the payment and sends the payment status as a response to the Boxo JS SDK pay event.
    • During this process, the host app server sends a callback to the Boxo Platform, which updates the order status in the miniapp backend.
  11. The miniapp processes the payment status received from the Boxo JS SDK event and confirms the order payment status on its server.
  12. The miniapp finalizes the order payment status.
    • If the order is still processing at this stage, the miniapp will request the payment status from the Boxo Platform, which, in turn, will query the host app server for an update.
  13. Finally, the miniapp displays the order payment status page to the user.

Setting up the backend

*Note: Feature must be enabled in Dashboard Partnership AuthDiagram Data format Currently, JSON is used for data exchange. Decimal values can be represented as floats, numbers, or strings, with a maximum of 20 digits in total and up to 2 digits after the decimal point. String size limits are defined within the data type specifications.

1. “Create Order Payment” Endpoint

This endpoint allows the Boxo Platform to create an order payment (refer to Step 3 in the diagram). URL and METHOD: This endpoint must handle a HTTPS POST request URL to endpoint must be provided in Dashboard AuthDiagram Headers:
KeyValue
Authorization<prefix> <base64 encoded(hostapp_client_id:hostapp_secret_key)> or token from Get access token can be used instead
Content-typeapplication/json
X-User-ID<Hostapp user reference>
  • Default <prefix>: Token. Access token prefix can be set in Boxo Connect.
  • To use user access token from Boxo Connect as authorization token enable Use access token in Dashboard.
  • hostapp_client_id and hostapp_secret_key must be provided in Dashboard
Body
FieldData typeDescription
app_idStringMiniapp identifier
orderOrderDetailsOrder info (amount, currency and etc.)
OrderDetails:
FieldData typeOptionalDescription
currencyString(20)NoCurrency code for the order (e.g., ‘USD’)
amountDecimalNoTotal order amount (decimal with 2 decimal places)
subtotal_amountDecimalYesSubtotal before taxes and shipping (decimal with 2 decimal places)
shipping_amountDecimalYesShipping cost (decimal with 2 decimal places)
discount_amountDecimalYesTotal discount applied (decimal with 2 decimal places)
tax_titleString(250)YesTitle/name of the tax
tax_amountDecimalYesTax amount (decimal with 2 decimal places)
taxes_includedBooleanYesBoolean indicating if taxes are included in the price
noteTextYesAdditional notes for the order
custom_attributesJSONYesJSON field for custom order attributes
miniapp_order_idString(255)YesOrder ID from the miniapp side
hostapp_user_idString(255)YesUser ID from the host app
itemsOrderItem[]YesList of order items (see OrderItem below)
shipping_addressObjectYesShipping address details (see OrderShippingAddress below)
OrderItem:
FieldData typeOptionalDescription
product_nameString(250)NoName of the product
product_variant_nameString(250)NoProduct variant name
product_skuString(250)NoProduct SKU/identifier
product_image_urlString(500)NoURL to product image
quantityIntegerNoQuantity of the product (positive integer)
priceDecimalNoPrice per item (decimal with 2 decimal places)
discountDecimalNoDiscount per item (decimal with 2 decimal places)
OrderShippingAddress:
FieldData typeOptionalDescription
address1String(1000)YesPrimary address line
address2String(1000)YesSecondary address line
first_nameString(250)YesFirst name
last_nameString(250)YesLast name
phoneString(250)YesPhone number
region_nameString(100)YesRegion/state name
region_codeString(100)YesRegion/state code
province_nameString(250)YesProvince name
province_codeString(250)YesProvince code
cityString(250)YesCity name
countryString(250)YesCountry name
postal_codeString(250)YesPostal/ZIP code
latitudeDecimalYesGeographic latitude (-90 to 90, 15 decimal places)
longitudeDecimalYesGeographic longitude (-180 to 180, 15 decimal places)
Response:
  • Response status must be 200 in all cases
  • Response body:
FieldData typeOptionalDescription
order_payment_idString(250)No, except error_code providedOrder payment identifier
error_codeStringYesIf some error is occured error code should be provided. Example: {"error_code": "INVALID_ORDER_DATA"} All error codes can be found here
custom_attributesJSONYesJSON field for custom order attributes
Request Example:
    curl --location --request POST '[YOUR_SERVER_URL]/api/create-order-payment/'\
    --header 'Content-Type: application/json' \
    --header 'Authorization: Basic {{BASE64_ENCODED_CLIENT_ID_AND_CLIENT_SECRET}}' \
    --data-raw '{ "app_id": "{{MINIAPP_ID}}", "order": {
        "amount": "100.00",
        "currency": "USD",
        "miniapp_order_id": "{{ MINIAPP_ORDER_ID }}",
        "hostapp_user_id": "{{ HOSTAPP_USER_ID }}"
    }}'

2. “Get Order Payment Status” Endpoint

This endpoint is for Boxo platform to get order payment status URL and METHOD This endpoint must handle a HTTPS POST request Can be configured to be a GET method URL to endpoint must be provided in Dashboard AuthDiagram Headers
KeyValue
Authorization<prefix> <base64 encoded(hostapp_client_id:hostapp_secret_key)> or token from Get access token can be used instead
Content-typeapplication/json
X-User-ID<Hostapp user reference>
Default <prefix>: Token. Access token prefix can be set in Boxo Connect. To use user access token from Boxo Connect as authorization token enable Use access token in Dashboard. hostapp_client_id and hostapp_secret_key must be provided in Dashboard Body
FieldData typeDescription
app_idStringMiniapp identifier
client_idStringHostapp identifier
order_payment_idStringOrder payment identifier
Response
  • Response status must be 200 in all cases
  • Response body
FieldData typeOptionalDescription
app_idStringNo, except error_code providedMiniapp identifier
client_idStringNo, except error_code providedHostapp identifier
order_payment_idStringNo, except error_code providedOrder payment identifier
payment_statusString(100)No, except error_code providedOrder payment status: in_process, paid, cancelled, failed
payment_fail_reasonStringYesOrder payment fail reason
custom_attributesJSONYesJSON field for custom order attributes
error_codeStringYesIf some error is occured error code should be provided. Example: {"error_code": "ORDER_NOT_FOUND"} All error codes can be found here
Request Example
    curl --location --request POST '[YOUR_SERVER_URL]/api/get-order-payment-status/'\
    --header 'Content-Type: application/json' \
    --header 'Authorization: Basic {{BASE64_ENCODED_CLIENT_ID_AND_CLIENT_SECRET}}' \
    --data-raw '{ "app_id": "{{MINIAPP_ID}}", "client_id": "{{CLIENT_ID}}", "order_payment_id": "{{ORDER_PAYMENT_ID}}"}'

3. Webhook “Complete Order Payment”

This endpoint is part of the Boxo Platform. It sends a request to the miniapp server to complete the order payment after the payment has been processed (refer to the step after 10 in the diagram). URL and METHOD This is HTTPS POST /api/v1/orders/complete-order/ endpoint IP address of requesting services must be provided in Dashboard for whitelisting or Request Signaturing must be enabled Headers
KeyValue
Content-typeapplication/json
Body
FieldData typeOptionalDescription
order_payment_idString(250)NoOrder payment identifier
app_idStringNoMiniapp identifier
client_idStringNoHostapp identifier
payment_statusString(100)NoOrder payment status: in_process, paid, cancelled, failed
payment_fail_reasonStringYesOrder payment fail reason
custom_attributesJSONYesOrder custom attributes
Response:
  • Response status will be 400 in case there is error_code
  • Response body:
FieldData typeDescription
codeStringRequest result code example: SUCCESS
messageStringResult message
error_codeStringError code
error_messageStringResult error message. All error codes can be found here
Request Example
    curl --location --request POST '[BOXO_PLATFORM_SERVER_URL]/api/v1/orders/complete-order/' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "order_payment_id": "{{ORDER_PAYMENT_ID}}",
        "payment_status": "paid",
        "client_id": "{{CLIENT_ID}}",
        "app_id": "{{MINIAPP_ID}}"
    }'
Response Example
    {
        "code": "SUCCESS",
        "message": "Success"
    }
    {
        "error_code": "ORDER_NOT_FOUND",
        "error_message": "No Order matches the given query."
    }

Setting up the SDK

Listening for Payment event

iOS

miniapp.delegate = self
...
extension ViewController : MiniappDelegate {
    func didReceivePaymentEvent(miniapp: Miniapp, paymentData: PaymentData) {
        // Show payment processing screen and handle payment according to paymentData 
        // paymentData.amount
        // paymentData.miniappOrderId 
        // paymentData.currency
        // Once payment is done, modify payment data and send result to mini app: 
        paymentData.status = "success" // change the payment status. "failed" in case payment failed, "cancelled" in case payment cancelled
        miniapp.sendPaymentEvent(paymentData: paymentData)
    }
}

Android

Kotlin
    Boxo.getMiniapp("[APP_ID]")
        .setPaymentEventListener { boxoActivity: BoxoActivity, miniapp, paymentData ->
            // Show payment processing screen and handle payment according to paymentData 
            // paymentData.amount
            // paymentData.miniappOrderId 
            // paymentData.currency
            // Once payment is done, modify payment data and send result to mini app: 
            paymentData.status = "success" // change the payment status. "failed" in case payment failed, "cancelled" in case payment cancelled
            miniapp.sendPaymentResult(paymentData)
        }.open(this)

Flutter

Important To show payment processing on Flutter, need to enable ‘multitaskMode’. Miniapp opens on a native screen. To show payment processing need to hide miniapp screen. Boxo.setConfig('[CLIENT_ID]', multitaskMode: true);
  @override
  void initState() {
    paymentSubscription = Boxo.paymentEvents().listen((PaymentEvent payment) async {
      Boxo.hideMiniapps(); // need to hide the miniapp before showing the payment page or popup
      // Show payment processing screen and handle payment according to paymentData
      // payment.amount
      // payment.miniappOrderId 
      // payment.currency
      // payment.transactionToken
      // Once payment is done, modify payment data and send result to miniapp: 
      NDialog(
        dialogStyle: DialogStyle(titleDivider: true),
        title: Text("Payment"),
        content: Text("Confirm payment"),
        actions: <Widget>[
          TextButton(
              child: Text("Confirm"),
              onPressed: () {
                Navigator.pop(context);
                //.. send request to handle payment to your backend
                payment.status = 'success'; // change the payment status. "failed" in case payment failed, "cancelled" in case payment cancelled
                Boxo.sendPaymentEvent(payment); // send payment result to miniapp
                Boxo.openMiniapp(payment.appId); // need to open the miniapp
              }),
          TextButton(
              child: Text("Cancel"),
              onPressed: () {
                Navigator.pop(context);
                payment.status = 'cancelled';
                Boxo.sendPaymentEvent(payment);
                Boxo.openMiniapp(payment.appId);
              }),
        ],
      ).show(context);
    });
    Boxo.openMiniapp('[APP_ID]');
    super.initState();
  }

React Native

Important To show payment processing on React Native, need to enable ‘multitaskMode’. Miniapp opens on a native screen. To show payment processing need to hide miniapp screen. Boxo.setConfig('[CLIENT_ID]', { enableMultitaskMode: true })
  useEffect(() => {
    const paymentEventsSubscription = Boxo.paymentEvents.subscribe(
          (event) => {
              // hide miniapp to return to the react-native app page
             Boxo.hideMiniapps();
              //payment data
             const appId = event.app_id;
             const paymentEvent = event.payment_event;
             const transactionToken = paymentEvent.transaction_token;
             const amount = paymentEvent.amount;
             const currency = paymentEvent.currency;
             const extraParams = paymentEvent.extra_params;

             // display payment screen
             // after payment is completed
             // open hidden miniapp 
             Boxo.openMiniapp(appId);

             //send payment result to miniapp
             const newEvent = {
               app_id: appId,
               payment_event: {
                 ...event.payment_event,
                 status: // change the payment status. "failed" in case payment failed, "cancelled" in case payment cancelled
               },
             };
             Boxo.paymentEvents.send(newEvent);
          },
          () => {},
        );
    return () => {
            ...
            paymentEventsSubscription();
        };
  }, []);

Expo

Important To show payment processing on Expo, need to enable ‘multitaskMode’. Miniapp opens on a native screen. To show payment processing need to hide miniapp screen. Boxo.setConfig('[CLIENT_ID]', { enableMultitaskMode: true })
Boxo.addPaymentEventListener((paymentData) => {
  // Show payment processing screen and handle payment according to paymentData 
  // paymentData.amount
  // paymentData.miniappOrderId 
  // paymentData.currency
  // Once payment is done, modify payment data and send result to mini app: 
  Boxo.hideMiniapps();
  paymentData.status = "success"; // change the payment status. "failed" in case payment failed, "cancelled" in case payment cancelled
  Boxo.sendPaymentEvent(paymentData);
  Boxo.openMiniapp({ appId: paymentData.appId })
});