https://github.com/multisafepay/pos-android-integration
https://github.com/multisafepay/pos-android-integration
Last synced: 30 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/multisafepay/pos-android-integration
- Owner: MultiSafepay
- Created: 2022-12-22T10:17:46.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2025-08-14T10:31:33.000Z (12 months ago)
- Last Synced: 2025-08-14T12:24:54.338Z (12 months ago)
- Language: Java
- Size: 11.3 MB
- Stars: 0
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# POS Android Integration
## Overview
This repository describes how to integrate a third-party Android application with the MultiSafepay Pay App using Android Intents (App-to-App communication).
---
## Integration Types
The Pay App supports two execution environments:
* **SmartPOS** → Devices with payment kernel (e.g. Sunmi)
* **Tap to Pay** → Android devices without kernel, using NFC
Both use the same App-to-App integration model.
---
## Pay App Packages
* `com.multisafepay.pos.sunmi` → SmartPOS (kernel devices)
* `com.multisafepay.pos.nokernels` → Tap to Pay (non-kernel devices)
---
## Selecting the Pay App
Use the correct package depending on the device type.
```java
private String getMSPPackage(boolean isTapToPayDevice) {
if (isTapToPayDevice) {
return "com.multisafepay.pos.nokernels";
} else {
return "com.multisafepay.pos.sunmi";
}
}
```
---
## Manifest Configuration
```xml
```
---
## Callback Handling
```java
@Override
protected void onNewIntent(Intent intent) {
processMSPMiddlewareResponse(intent);
super.onNewIntent(intent);
}
private void processMSPMiddlewareResponse(@NonNull Intent intent) {
if (intent.hasExtra("status")) {
int status = intent.getIntExtra("status", 0);
String message = intent.getStringExtra("message");
handleMiddlewareCallback(status, message);
}
}
private void handleMiddlewareCallback(int status, String message) {
switch (status) {
case 875:
receivedCallbackIntent("EXCEPTION " + message);
break;
case 471:
receivedCallbackIntent("COMPLETED " + message);
break;
case 17:
receivedCallbackIntent("CANCELLED " + message);
break;
case 88:
receivedCallbackIntent("DECLINED " + message);
break;
}
}
private void receivedCallbackIntent(String message) {
Intent intent = new Intent(this, PaymentActivity.class);
intent.putExtra("message", message);
intent.putExtra("description", "pass data to this Activity");
startActivity(intent);
}
```
---
## Payment Flows
### 1. Standard (Legacy) Flow
#### Order Items
```java
JSONArray jsonArray = new JSONArray();
try {
JSONObject item1 = new JSONObject();
item1.put("name", "Product 1");
item1.put("unit_price", "0.10");
item1.put("quantity", "1");
item1.put("merchant_item_id", "749857");
item1.put("tax", "3.90");
JSONObject item2 = new JSONObject();
item2.put("name", "Product 2");
item2.put("unit_price", "0.20");
item2.put("quantity", "1");
item2.put("merchant_item_id", "749857");
item2.put("tax", "1.40");
jsonArray.put(item1);
jsonArray.put(item2);
} catch (JSONException e) {
e.printStackTrace();
}
```
---
#### Sending Payment Intent
```java
boolean isTapToPayDevice = false; // Implement your own device detection
String packageName = getMSPPackage(isTapToPayDevice);
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
if (intent != null) {
if (!isTapToPayDevice) {
intent.setClassName(packageName,
"com.multisafepay.pos.middleware.IntentActivity");
}
// amount must be long and expressed in minor units (for example: cents)
long amountInCents = amount;
intent.putExtra("items", jsonArray.toString());
intent.putExtra("order_id", getOrderId());
intent.putExtra("order_description", "info about the order");
intent.putExtra("currency", "EUR");
intent.putExtra("amount", amountInCents);
intent.putExtra("package_name", getPackageName());
startActivity(intent);
}
```
---
### 2. E-commerce Flow
#### Order Items
```java
JSONArray jsonArray = new JSONArray();
try {
JSONObject socks = new JSONObject();
socks.put("name", "Socks");
socks.put("description", "One pair of black socks");
socks.put("merchant_item_id", "001-M");
socks.put("unit_price", 0.105785124);
socks.put("quantity", 3);
socks.put("tax_table_selector", "21_percent");
JSONObject shipping = new JSONObject();
shipping.put("name", "Shipping");
shipping.put("description", "Domestic shipping (zone 1)");
shipping.put("merchant_item_id", "msp-shipping");
shipping.put("unit_price", 0.15);
shipping.put("quantity", 1);
jsonArray.put(socks);
jsonArray.put(shipping);
} catch (JSONException e) {
e.printStackTrace();
}
```
---
#### Sending Payment Intent
```java
boolean isTapToPayDevice = false; // Implement your own device detection
String packageName = getMSPPackage(isTapToPayDevice);
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
if (intent != null) {
if (!isTapToPayDevice) {
intent.setClassName(packageName,
"com.multisafepay.pos.middleware.IntentActivity");
}
setCheckoutOptions(intent);
// amount must be long and expressed in minor units (for example: cents)
long amountInCents = amount;
intent.putExtra("items", jsonArray.toString());
intent.putExtra("order_id", getOrderId());
intent.putExtra("description", "info about the order");
intent.putExtra("currency", "EUR");
intent.putExtra("amount", amountInCents);
intent.putExtra("reference", "Ref-" + System.currentTimeMillis());
intent.putExtra("auto_close", false);
intent.putExtra("package_name", getPackageName());
startActivity(intent);
}
```
---
### Checkout Options
```java
private void setCheckoutOptions(Intent intent) {
try {
JSONObject checkoutOptions = new JSONObject();
checkoutOptions.put("validate_cart", true);
JSONObject taxTables = new JSONObject();
checkoutOptions.put("tax_tables", taxTables);
JSONObject defaultTaxTable = new JSONObject();
defaultTaxTable.put("rate", 0);
taxTables.put("default", defaultTaxTable);
intent.putExtra("checkout_options", checkoutOptions.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
```
---
## Unreferenced Refund Flow
```java
private void sendRefundIntent(long amountInCents, boolean isTapToPayDevice) {
String packageName = getMSPPackage(isTapToPayDevice);
Intent intent = getPackageManager()
.getLaunchIntentForPackage(packageName);
if (intent != null) {
if (!isTapToPayDevice) {
intent.setClassName(
packageName,
"com.multisafepay.pos.middleware.IntentActivity"
);
}
intent.putExtra("order_id", "REFUND_" + System.currentTimeMillis());
intent.putExtra("amount", amountInCents);
intent.putExtra("refund", true);
intent.putExtra("package_name", getPackageName());
startActivity(intent);
}
}
```
---
## Tap to Pay Notes
Tap to Pay uses the same App-to-App integration model as SmartPOS, but the entry point is different.
* Same Intent structure
* Same parameters
* Same callback handling
For **SmartPOS** (`com.multisafepay.pos.sunmi`), the payment flow is started directly via:
`com.multisafepay.pos.middleware.IntentActivity`
For **Tap to Pay** (`com.multisafepay.pos.nokernels`), the app should be launched using the package launcher intent:
`getLaunchIntentForPackage("com.multisafepay.pos.nokernels")`
In this case, the app handles the incoming intent through its launcher flow before navigating to the payment screen.
Important:
* Use `setClassName(..., "com.multisafepay.pos.middleware.IntentActivity")` only for SmartPOS
* Do not force `IntentActivity` for Tap to Pay
* `amount` must be sent as `long` in minor units
* `package_name` must be the package name of the third-party app that should receive the callback intent