Deep payment
When a customer buys something inside the Cheggout marketplace, your app can execute the payment rather than Cheggout. The SDK hands you the payment details, you collect the money with your own rails, and you relaunch the SDK with the result.
Available Android · Available iOS
Why you'd want this
For a bank or wallet, payments are the thing you do best. Keeping them in your own flow means the customer pays with the account and authentication they already trust, you keep the transaction on your rails, and the purchase appears in your statement rather than as an opaque third-party charge.
The flow
Receiving the request
- Android
- iOS
Register a payment manager and implement the listener:
private lateinit var chegPaymentManager: CHEGPaymentManager
class CGMainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
chegPaymentManager = CHEGPaymentManager()
chegPaymentManager.setCallback(object : CHEGPaymentListener {
override fun initPaymentCallFromCheggout(responseString: String?) {
val decoded = String(Base64.decode(responseString, Base64.DEFAULT))
val json = JSONObject(decoded)
val deepLink = json.optString("deepLink", "")
// Collect the payment with your own rails, then relaunch:
val payload =
"{'Component':'CHEGGOUT','IsEmployee':'1','Language':'EN','DeeplinkInfo':'$deepLink'}"
launchCheggout(this@CGMainActivity, virtualId, sessionId, payload)
finish()
}
})
}
}
The same decode, written out as a helper:
fun decodeBase64(value: String?): String {
val decodedBytes = Base64.decode(value, Base64.DEFAULT)
return String(decodedBytes, Charsets.UTF_8)
}
The payment data arrives in the exitHandler when the SDK exits:
cheggoutApp = CheggoutApp(
userID: virtualId,
sessionInfo: sessionId,
accessInfo: nil,
payload: payload,
exitHandler: { paymentData in
if let paymentData = paymentData {
// Decode, collect the payment, then relaunch
print("PaymentData received from sdk:", paymentData)
}
}
)
Decoding helper:
extension String {
func decodeBase64() -> String? {
guard let data = Data(base64Encoded: self) else { return nil }
return String(data: data, encoding: .utf8)
}
}
if let decodedPaymentData = paymentDataValue.decodeBase64() {
print("Decoded payment response JSON:", decodedPaymentData)
}
The payload
The response string is Base64. Decoded, it looks like this:
{
"deepLink": "https://click.cheggout.com?type=pwa&id=<destination>?token=tokenvalue/cheggoutRefNo=YOURCHANNEL1775814852490048/paymentType=YOURCHANNEL/amount=540.00/transactionId=bankTransactionID/status=bankStatus",
"cheggoutRefNo": "YOURCHANNEL1775814852490048",
"amount": "54000",
"responseCode": "COLLECTPAYMENT",
"virtualId": "pub_8f14e45fceea167a",
"merchantName": "Cheggout",
"merchantId": "REPLACE_MERCHANT_ID"
}
| Field | Meaning |
|---|---|
deepLink | Used to relaunch the SDK. Substitute bankTransactionID with your transaction ID and bankStatus with the payment status. |
cheggoutRefNo | Cheggout's unique reference for this transaction |
amount | The payment amount, in paisa |
responseCode | COLLECTPAYMENT |
virtualId | The user |
merchantName | Identifies the transaction source within Cheggout |
merchantId | Unique per merchant name, issued by the bank at onboarding |
amount is in paisa"54000" means ₹540.00. Charging the raw value as rupees overcharges by a hundred times.
Convert explicitly and unit-test the conversion.
Relaunching
After the payment completes, relaunch the SDK with the deep link — with two substitutions made:
| Placeholder in the deep link | Replace with |
|---|---|
bankTransactionID | Your transaction identifier for this payment |
bankStatus | The payment status |
Pass the resulting deep link in the launch payload's DeeplinkInfo field. The SDK opens on the
confirmation flow and settles the order with Cheggout.
Relaunch on both success and failure. A failed payment still needs to be reported so the order does not hang in a pending state.
The Client Status API
Cheggout may need to check a payment's status independently — for instance if your app was killed before it could relaunch. For that you expose an endpoint Cheggout calls:
GET https://<your-domain>/cheggout-txn-status?virtualId={{virtualId}}&orderId={{cheggoutRefNo}}&amount={{amount}}
With header Authorization: {{key}}.
It must work with either your transaction ID plus virtualId, or cheggoutRefNo plus virtualId —
because in some scenarios Cheggout will not have received your transaction ID.
Cheggout encrypts virtualId, cheggoutRefNo and amount before appending them, using AES with
a shared secret converted to a 16-byte key. Your endpoint must decrypt before use.
TBD The secret-key exchange process is not documented. Agree it with Cheggout before implementing. See Client Status API.
Implementation checklist
- Payment callback registered before the SDK can invoke it
- Base64 decoding tested against a real payload
- Amount converted from paisa and unit-tested
-
cheggoutRefNostored against your transaction, for reconciliation - Deep link substitution correct for both
bankTransactionIDandbankStatus - Relaunch happens on success and on failure
- Client Status API implemented, with decryption
- Reconciliation matches your payments to
cheggoutRefNo
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Customer charged 100× | amount treated as rupees rather than paisa |
| Order stuck pending | SDK not relaunched after payment, or placeholders not substituted |
| Relaunch opens the wrong screen | Deep link not passed in DeeplinkInfo |
| Cheggout cannot verify a payment | Client Status API missing, or decryption failing |