Process a Refund or Cancellation
This guide explains how to initiate a refund or cancel an existing payment transaction using the Tap on Phone application. Both operations require the original transaction ID and use a similar Intent structure to standard purchases.
Understanding Refunds vs. Cancellations
While both operations return funds to the customer, they have different use cases and restrictions:
- Cancellation: Reverses a transaction. The transaction must be valid, approved, and not already refunded or cancelled. The user logged into the app must have administrator access.
- Refund: Returns funds to the customer after the transaction is fully processed. The transaction must be valid, approved, and not already refunded or cancelled. The user logged into your application must have administrator access to perform a refund.
Prerequisites
Before you process a refund or cancellation, ensure you have:
- An initialized Tap on Phone application.
- The
userId,userToken, andmerchantIdassociated with the current session. - The
transactionId(from the Tap on Phone system) of the original approved payment. - Configured your backend to handle Single Sign-On (SSO)
refundauthorization requests (see Backend Integration: Handling SSO Requests).
Step 1: Register the Activity Result Launcher
Register an Activity Result callback to handle the outcome of the refund or cancellation process.
val modificationResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val data = result.data
if (result.resultCode == Activity.RESULT_OK && data != null) {
val status = data.getStringExtra("status") ?: "None"
if (status == "APPROVED") {
println("Transaction successfully modified (Refunded/Canceled).")
// Extract receipt data for the refund/cancellation
} else {
val declineCause = data.getStringExtra("declineCause") ?: "Unknown"
println("Modification declined: $declineCause")
}
} else if (result.resultCode == Activity.RESULT_CANCELED) {
val errorCode = data?.getStringExtra("errorCode") ?: "None"
val errorMessage = data?.getStringExtra("errorMessage") ?: "User Canceled"
println("Operation failed or was canceled: $errorMessage ($errorCode)")
}
}Step 2: Prepare the SSO Metadata
Because refunds are sensitive operations, the Tap on Phone backend validates them against your backend using SSO. You must pass your Client ID in the operationMetadata field so the platform can route the authorization request properly.
// Format your Client ID as a JSON string
val operationMetadata = "{\"ClientID\\":\"your-client-id-here\"}"Step 3: Construct and Launch the Intent
Create an explicit intent targeting the POSActivity in the Tap on Phone application.
You must specify the transactionType (either REFUND or CANCEL) and provide the identifier of the transaction you wish to modify in the originalTransaction field.
private fun performRefundOrCancel(originalTransactionId: Int, isRefund: Boolean) {
val intent = Intent().apply {
setClassName(
"com.dejamobile.cbp.sps.app",
"com.dejamobile.cbp.sps.app.POSActivity"
)
// Session Identifiers
putExtra("userId", userId)
putExtra("userToken", userToken)
putExtra("merchantId", merchantId)
// Specify the type: "REFUND" or "CANCEL"
val type = if (isRefund) "REFUND" else "CANCEL"
putExtra("transactionType", type)
// Pass the ID of the transaction to modify
putExtra("originalTransaction", originalTransactionId)
// Required for SSO authorization
putExtra("operationMetadata", operationMetadata)
}
// Launch the intent
modificationResultLauncher.launch(intent)
}Step 4: Handle the Result
Once you launch the intent, the Tap on Phone application takes over. For a refund, the merchant may be prompted to present the original payment card.
When the operation concludes, your modificationResultLauncher callback executes. Inspect the status field to verify the refund or cancellation was approved, and update your internal order records accordingly.
Next Steps
- Learn how to extract and parse the receipt data returned by these operations in Handle Transaction Results.