Skip to main content

Simulating Payment Failures

Testing failure states is just as important as testing successful purchases. You must ensure that declined cards, cancelled PIN prompts, and insufficient funds do not grant free access or leave users stranded with frozen screens.

[1. Enter Decline Test Card] ➔ [2. Provider Returns Decline] ➔ [3. SolydFlow Commits FAILED_DECLINED] ➔ [4. App Displays Retry UI]

Failure Trigger Matrix Across Gateways​

Use these test card numbers and simulator prompts to test specific failure conditions:

Failure ScenarioGatewayTest Input / Card NumberCVV / PINExpected State
Card DeclinedPaystack4084 0841 0000 0001408FAILED_DECLINED
Insufficient BalancePaystack4084 0841 0000 0002408FAILED_DECLINED
Generic Card DeclineStripe4000 •••• •••• 0002123FAILED_DECLINED
Insufficient FundsStripe4000 •••• •••• 0996123FAILED_DECLINED
Declined CardFlutterwave4444 •••• •••• 4441123FAILED_DECLINED
User Cancelled PINM-PesaSelect "Cancel" on SIM simulatorResultCode: 1032FAILED_DECLINED
Wrong M-Pesa PINM-PesaEnter invalid 4-digit PINResultCode: 2001FAILED_DECLINED

How to Simulate Failures in the Simulator​

  1. Open the SolydFlow Console and navigate to Solyd Simulator.
  2. Ensure Execution Mode is set to Test (Sandbox).
  3. Select your simulated currency (e.g. NGN, USD, or KES).
  4. Click Simulate Checkout on your package.
  5. In the payment modal, enter one of the decline cards from the matrix above (e.g., Paystack 4084 0841 0000 0001).
  6. Click Pay. The gateway will immediately reject the payment.

Post-Failure Verification Checklist​

After simulating a decline, verify the following in the SolydFlow Console:

┌───────────────────────────────────────┬────────────────────────────────────────────────────────┐
│ Console Checkpoint │ Expected Behavior │
├───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ **1. Transactions Ledger** │ State is recorded as **`FAILED_DECLINED`**. │
├───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ **2. Customer Directory** │ Entitlement remains **`null` / inactive**. │
├───────────────────────────────────────┼────────────────────────────────────────────────────────┤
│ **3. Outbound Webhooks** │ **No** `subscription_started` webhook was fired. │
└───────────────────────────────────────┴────────────────────────────────────────────────────────┘

Handling Failures in Application Code​

Your client application should handle the three possible checkout outcomes cleanly:

lib/screens/checkout_screen.dart
Future<void> handlePurchase(BuildContext context, String packageId) async {
try {
final CustomerInfo? info = await SolydFlow.purchasePackage(context, packageId);

// Case 1: User voluntarily dismissed or cancelled modal
if (info == null) {
debugPrint("User dismissed the checkout sheet.");
return;
}

// Case 2: Success
if (info.activeEntitlements["pro_access"] == true) {
Navigator.pop(context);
}
} catch (error) {
// Case 3: Gateway Declined / Network Failure
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Payment unsuccessful: $error"),
backgroundColor: Colors.red,
action: SnackBarAction(
label: "Try Another Card",
onPressed: () => handlePurchase(context, packageId),
),
),
);
}
}