Skip to main content

Payments SDK Integration Guide for Android

Introduction

This guide explains how to integrate the AppInChina Payments SDK into your Android application. With this SDK, you can accept payments through WeChat Pay and Alipay.

This guide assumes basic familiarity with Android development and Java.

Download

Download the latest Android SDK package: payments_sdk_latest.zip


1. Install the SDK

Add the Payments SDK to your app's build.gradle file:

dependencies {
// The AAR is shipped inside /downloads/payments_sdk_latest.zip
implementation(name: 'payments_sdk_v20220912', ext: 'aar')
}

You also need to ensure Gradle can resolve local AARs. Add this to your module (app) build.gradle:

repositories {
flatDir {
dirs 'libs'
}
}

Then place the file payments_sdk_v20220912.aar into app/libs/.


2. Initialize the SDK

In your Application class:

  1. Import the SDK classes:
import com.mandou.acp.sdk.AcpClient;
import com.mandou.acp.sdk.AcpClientConfig;
  1. Initialize the SDK in onCreate():
@Override
public void onCreate() {
super.onCreate();
AcpClient.sharedInstance().init(
this,
new AcpClientConfig("YOUR_APP_ID", "YOUR_APP_SECRET")
);
}

To get your APP_ID and APP_SECRET, start with the credential request step in the Prerequisites and environment setup guide.


3. Initialize payment tools

When your payment screen is loaded, you should initialize payment tools:

AcpClient.sharedInstance().initPayTools(new PayToolCallback() {
@Override
public void onSuccess(String payChannel) {
if ("WECHAT".equalsIgnoreCase(payChannel)) {
initWechat();
} else if ("ALIPAY".equalsIgnoreCase(payChannel)) {
initAlipay();
}
}

@Override
public void onFail(String s, Throwable throwable) {
Toast.makeText(PayActivity.this, "Payment environment initialization failed", Toast.LENGTH_LONG).show();
}
});
tip

Always call initPayTools() when the payment screen loads. It fetches the signed/available pay channels for your app (e.g., WeChat vs Alipay) and initializes the SDK state used to decide which payment buttons to show. Skipping it commonly results in missing buttons or startPayment() failing due to incomplete environment setup.


4. Create payment buttons and start payment flow

Once the payment environment is initialized, you can create buttons to trigger payments.

Each button will:

  • Call buildPayOrder() to prepare the payment information.
  • Call startPayment() to begin the payment process through the SDK.

We recommend creating one button for each payment method your app supports.

Example — WeChat Pay button

private void initWechat() {
Button wechatBtn = findViewById(R.id.btn_pay_wechat);
wechatBtn.setVisibility(View.VISIBLE);

wechatBtn.setOnClickListener(v -> {
PayOrder payOrder = buildPayOrder("WECHAT");
AcpClient.sharedInstance().startPayment(
PayActivity.this,
payOrder,
PayResultActivity.class,
PayActivity.this
);
});
}

Example — Alipay button

private void initAlipay() {
Button alipayBtn = findViewById(R.id.btn_pay_alipay);
alipayBtn.setVisibility(View.VISIBLE);

alipayBtn.setOnClickListener(v -> {
PayOrder payOrder = buildPayOrder("ALIPAY");
AcpClient.sharedInstance().startPayment(
PayActivity.this,
payOrder,
PayResultActivity.class,
PayActivity.this
);
});
}
caution

The third parameter in startPayment() (resultActivityClass) must not be null if you want to handle payment results visually.

If set to null, the SDK will not show a result page automatically. You must handle post-payment behavior manually.

note

If WeChat or Alipay is not installed on the user's device, the SDK will automatically detect this and display a helpful message (e.g., "WeChat not installed") before attempting to launch the payment app.


5. Building a pay order: the PayOrder class

The buildPayOrder() function creates the PayOrder object needed to start a payment.

A PayOrder includes:

  • Payment amount
  • Product title
  • Unique business order number
  • Payment method (WeChat or Alipay)
  • Optional additional data

Example — building a PayOrder

private PayOrder buildPayOrder(String payChannel) {
PayOrder payOrder = PayOrder.payWith(payChannel);
payOrder.setAmount(new BigDecimal(amountStr).multiply(new BigDecimal(100)).longValue());
payOrder.setBizNo(bizNoStr);
payOrder.setGoodsTitle(titleStr);
payOrder.setCustomerIdentity(customerIdStr);
payOrder.setAttachData(extraDataMap);
return payOrder;
}

PayOrder parameter reference

ParameterRequiredDescriptionExample
amountPayment amount (in cents)1000 (¥10.00)
bizNoUnique business order IDORDER20250427
goodsTitleProduct or service title"VIP Subscription"
payChannelPayment method (WECHAT or ALIPAY)"WECHAT"
customerIdentity🔶User identifier for order tracking"user_001"
attachDataExtra metadata if needed{EXPIRE_DATE: 2025-05-01}

attachData is the easiest way to link a payment back to your internal commerce model (SKU, plan, promo/campaign, etc.). It is recorded with the transaction and returned in order query results.

Example (key/value map)

Map<String, String> attachData = new HashMap<>();
attachData.put("productId", "pro_3m");
attachData.put("promoId", "winter25");
attachData.put("priceVersion", "v3");

payOrder.setAttachData(attachData);

Best practices

  • Keep keys stable and documented (so analytics and support can rely on them).
  • Avoid PII and secrets.
  • Don’t use attachData as your only source of truth: your backend should still persist the internal order for bizNo and decide fulfillment only after verifying paymentStatus == PAID.

While customerIdentity is optional and payments will process without it, we strongly recommend setting and storing this value since it's essential for tracking order status and results.

Choosing values for bizNo, goodsTitle, and customerIdentity

FieldRecommendation
bizNoGenerate a unique string for each order. For example: \"{userId}_{timestamp}\" or \"ORDER_{UUID}\". This ensures idempotency and helps trace payment attempts.
goodsTitleProvide a short, meaningful description of the item or service being purchased. For example: \"Premium Subscription\" or \"Game Coins - 1000 Pack\".
customerIdentityUse a persistent user ID from your app or backend system. This helps with querying order history and handling disputes. For example: \"user_12345\" or \"openid_xyz\".

If you need help mapping your login system to customerIdentity, see: Login → Payments integration (customer identity).

6. Understanding the startPayment() method

Method overview

AcpClient.sharedInstance().startPayment(
context,
payOrder,
resultActivityClass,
callback
);

Parameters

ParameterDescriptionRequired?
contextThe current Activity context (e.g., this).
payOrderThe PayOrder object with all payment details.
resultActivityClassOptional Activity for showing a post-payment result screen. If you pass null, you should handle post-payment UX yourself.Optional
callbackOptional PayResultCallback to receive payment result events in your code.Optional but recommended

What happens after calling startPayment()

StepActionHandled By
1Validate PayOrder fieldsSDK
2Choose payment channelSDK
3Open WeChat/Alipay appSDK + Payment App
4User completes or cancels paymentPayment App
5Control returns to appSDK

7. Handling payment results in a custom activity

When you call startPayment(), you should pass an Activity class as the third parameter to present a custom result screen after the payment process completes. The SDK will redirect the user to that screen once the payment app returns control to your application.

caution

Do not treat client-side callbacks or “return-to-app” events as proof of payment success.

You must use querySingleOrder() (or the REST order-query endpoint) to confirm the final payment status using bizNo and customerIdentity.

7.1 Query single order

AcpClient.sharedInstance().querySingleOrder(
"customerIdentity",
"bizNo",
new PayOrderCallback() {
@Override
public void onSuccess(List<PayOrderInfo> list) {
if (!list.isEmpty() && "PAID".equals(list.get(0).getPaymentStatus())) {
// Payment succeeded
}
}

@Override
public void onFail(String s, Throwable throwable) {
// Handle error
}
}
);

The query result will contain a maximum of one PayOrderInfo object.

The PayOrderInfo class is structured as follows:

public class PayOrderInfo {
private String appId;
private String id;
private long amount;
private String bizNo;
private String goodsTitle;
private String payChannel;
private String customerIdentity;
private Map<String, String> attachData;
private String sourceFrom;
private Date pmtDt;
private String paymentStatus;
}

Important fields

  • paymentStatus:
    • Possible values:
      • PENDING — The payment is in progress or awaiting completion.
      • PAID — The customer has successfully paid for the order.
      • CLOSE — The payment was closed or canceled.
      • REFUND — The payment was refunded.
  • pmtDt: only populated when paymentStatus is PAID.

7.2 Implement a result activity

public class PayResultActivity extends AppCompatActivity {

private TextView resultText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pay_result);

resultText = findViewById(R.id.pay_result_text);

fetchPaymentStatus();
}

private void fetchPaymentStatus() {
String bizNo = PayToolInfo.getCurrentBizNo(); // Provided by the SDK
String customerId = "user_001"; // Store this when initiating the order

AcpClient.sharedInstance().querySingleOrder(
customerId,
bizNo,
new PayOrderCallback() {
@Override
public void onSuccess(List<PayOrderInfo> list) {
runOnUiThread(() -> {
if (!list.isEmpty() && "PAID".equals(list.get(0).getPaymentStatus())) {
resultText.setText("Payment Successful via " + list.get(0).getPayChannel());
} else {
resultText.setText("Payment Failed or Canceled");
}
});
}

@Override
public void onFail(String s, Throwable throwable) {
runOnUiThread(() -> {
resultText.setText("Failed to fetch payment result. Please try again.");
});
}
}
);
}
}
tip

We recommend using querySingleOrder() instead of relying on Intent extras, since payment results are not passed directly by the SDK.


8. Query single order via REST API

In addition to using querySingleOrder() on the client side, you can also verify or retrieve order details from your backend using a dedicated HTTP endpoint.

8.1 Endpoint

GET /detail.json
note

All requests must include APP_ID and APP_SECRET headers. See the Payments API reference for details.

8.2 Required headers

Header NameValueDescription
APP_IDYour App IDProvided by AppInChina Dashboard or operations team.
APP_SECRETYour App SecretProvided by AppInChina Dashboard or operations team.
Content-Typeapplication/jsonOptional but recommended.

8.3 Request parameters

ParameterTypeRequiredDescription
bizNostringThe unique order identifier (same value used in PayOrder.setBizNo()).
customerIdentitystringThe customer ID used during order creation (PayOrder.setCustomerIdentity()).

Example request

GET https://api.appinchinaservices.com/detail.json?bizNo=ORDER_123456789&customerIdentity=user_001

8.4 Successful response

{
"msg": "success",
"code": 0,
"data": {
"amount": 1000,
"appId": "yourAppId",
"bizNo": "ORDER_123456789",
"extInfo": {},
"gmtCreate": 1688000000000,
"gmtModified": 1688000000000,
"pmtDt": 1688000300000,
"attachData": {"EXPIRE_DATE": "2025-05-01"},
"sourceFrom": "wechat",
"goodsTitle": "VIP Subscription",
"id": "orderId123",
"payChannel": "WECHAT",
"paymentStatus": "PAID"
}
}
tip

Treat payment as successful only if paymentStatus is PAID.


9. Payment flow summary

App calls startPayment()

SDK opens payment app

User completes payment or cancels

Control returns to app

Custom result activity is launched

Activity uses bizNo and customerIdentity to query payment status

Backend confirms payment outcome

10. Query payment history

AcpClient.sharedInstance().queryHistoryOrder(
"customerIdentity",
"PAID",
1,
10,
new PayOrderCallback() {
@Override
public void onSuccess(List<PayOrderInfo> list) {
// Display payment history
}

@Override
public void onFail(String s, Throwable throwable) {
// Handle error
}
}
);

11. Refunds

The SDK does not provide any refund APIs.

To process refunds, log in to the AppInChina Dashboard and locate the specific order. From there, you can initiate and complete a refund. If you need assistance or have special cases, contact our operations team for support.