This guide explains the complete payment flow in Yabetoo, from creating a payment intent to receiving confirmation via webhooks.
Overview
The Yabetoo payment flow follows a two-step process that gives you full control over when and how payments are captured.
Step 1: Create a Payment Intent
A Payment Intent represents your intention to collect a payment. It tracks the payment lifecycle and stores information about the transaction.
const response = await fetch(
"https://pay.sandbox.yabetoopay.com/v1/payment-intents",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer sk_test_XXXXXXXXXXXXXXXXXXXXXXXX",
},
body: JSON.stringify({
amount: 5000,
currency: "xaf",
}),
}
);
const paymentIntent = await response.json();
// Returns: { id: "pi_xxx", client_secret: "...", amount: 5000, currency: "xaf" }
At this stage:
- No money has been moved
- The intent is in
pending status
- You have a
client_secret for the next step
Step 2: Confirm the Payment Intent
Confirmation triggers the actual payment. You provide the customer’s payment method details (Mobile Money information).
const response = await fetch(
"https://pay.sandbox.yabetoopay.com/v1/payment-intents",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer sk_test_XXXXXXXXXXXXXXXXXXXXXXXX",
},
body: JSON.stringify({
client_secret: paymentIntent.client_secret,
first_name: "John",
last_name: "Doe",
receipt_email: "[email protected]",
payment_method_data: {
type: "momo",
momo: {
country: "cg",
msisdn: "+242066594470",
operator_name: "mtn",
},
},
}),
}
);
After confirmation:
- The customer receives a push notification on their phone
- They enter their PIN to authorize the payment
- Yabetoo waits for the Mobile Money provider’s response
Step 3: Handle the Result
Synchronous Response
The confirmation request returns immediately with the payment status:
{
"intent_id": "pi_9aATHBv8UXuD6H5qrSav",
"amount": 5000,
"currency": "xaf",
"status": "succeeded",
"captured": true
}
Webhook Notification
For reliability, always listen for webhooks. Yabetoo sends an intent.completed event when the payment succeeds:
// Your webhook endpoint
app.post("/webhook", (req, res) => {
const event = req.body;
if (event.type === "intent.completed") {
const payment = event.data;
// Update your database
// Send confirmation to customer
}
res.status(200).send("OK");
});
Payment Statuses
| Status | Description |
|---|
pending | Payment intent created, not yet confirmed |
processing | Payment is being processed by the Mobile Money provider |
succeeded | Payment completed successfully |
failed | Payment failed (insufficient funds, rejected, etc.) |
Common Error Codes
When a payment fails, you may receive one of these error codes:
| Code | Description |
|---|
LOW_BALANCE_OR_PAYEE_LIMIT_REACHED_OR_NOT_ALLOWED | Insufficient funds, transaction limit reached, or transaction not allowed |
TIMEOUT | Customer did not confirm the payment in time |
Always handle the LOW_BALANCE_OR_PAYEE_LIMIT_REACHED_OR_NOT_ALLOWED error gracefully by informing the customer that their payment could not be processed due to insufficient balance or account restrictions.
Best Practices
- Always verify webhooks - Don’t rely solely on the synchronous response
- Store the payment intent ID - Link it to your order for tracking
- Handle failures gracefully - Show clear error messages to users
- Implement idempotency - Prevent duplicate payments on retries
Alternative: Checkout Sessions
For a simpler integration, use Checkout Sessions. Yabetoo handles the entire payment UI:
const session = await fetch("https://buy.api.yabetoopay.com/v1/sessions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer sk_test_XXXXXXXXXXXXXXXXXXXXXXXX",
},
body: JSON.stringify({
total: 5000,
currency: "xaf",
accountId: "acct_XXXXXXXX",
successUrl: "https://yoursite.com/success",
cancelUrl: "https://yoursite.com/cancel",
}),
});
// Redirect customer to session.url