Maya Checkout
How Maya hosted checkout sessions are created, how the user is redirected, and how the result is received.
Initiated via POST /api/v1/donations with payment_gateway: "maya".
Creating a Maya Checkout Session
Endpoint: POST /api/v1/donations
- Name
campaign_id- Type
- integer
- Description
Campaign to donate to
- Name
amount- Type
- number
- Description
Base donation amount in PHP (minimum: 1)
- Name
payment_gateway- Type
- string
- Description
Must be
"maya"for the Maya checkout path
- Name
is_anonymous- Type
- boolean
- Description
Default:
false
Request
{
"campaign_id": 1,
"amount": 1000,
"payment_gateway": "maya"
}
Response
{
"success": true,
"data": {
"redirectUrl": "https://payments-web-sandbox.maya.ph/...",
"transaction_id": "d60a890b-ade0-4a75-9127-...",
"reference_number": "550e8400-e29b-41d4-a716-..."
},
"message": "Payment session created successfully"
}
Maya Redirects
After payment, Maya redirects to the Batchmates backend:
| Outcome | Redirect target |
|---|---|
| Success | GET /api/v1/payments/maya/success?id={reference_number} |
| Failure | GET /api/v1/payments/maya/failure?id={reference_number} |
| Cancel | GET /api/v1/payments/maya/cancel?id={reference_number} |
The success handler calls verifyAndComplete() to confirm payment status with Maya's API before marking the donation complete, then redirects to {FRONTEND_URL}/donations/success.
Maya Webhook
Maya fires webhooks to POST /api/v1/payments/maya/webhook. The endpoint is protected by IP allowlist (sandbox: 13.229.160.234, 3.1.199.75; production: 18.138.50.235, 3.1.207.200).
Donations are marked completed by the webhook, not the redirect. The redirect calls verifyAndComplete() as a fallback only.
Internal Implementation
The checkout session is created inside MayaTransactionService::executeCheckout(), which calls MayaService to hit the Maya API.
// app/Services/MayaTransactionService.php
public function executeCheckout(Donation $donation): string
{
$fees = FeeCalculator::calculate('maya', $donation->amount, 'unknown');
$checkout = $this->mayaService->createCheckout([
'totalAmount' => [
'value' => $fees['total_amount'], // checkout API uses 'value'
'currency' => 'PHP',
],
'requestReferenceNumber' => $donation->reference_number,
'redirectUrl' => [
'success' => route('maya.success', ['id' => $donation->reference_number]),
'failure' => route('maya.failure', ['id' => $donation->reference_number]),
'cancel' => route('maya.cancel', ['id' => $donation->reference_number]),
],
'metadata' => [
'donation_id' => $donation->id,
'campaign_id' => $donation->campaign_id,
],
]);
$donation->update(['transaction_id' => $checkout['checkoutId']]);
return $checkout['redirectUrl'];
}
Maya's checkout API uses totalAmount.value while Maya's card payments API uses totalAmount.amount. This is an inconsistency in Maya's own API.
Mobile / Native Clients
The REST contract is platform-neutral — native apps call POST /api/v1/donations and receive the same redirectUrl. The browser-specific pieces map to mobile as follows:
- Open
redirectUrlin an in-app browser —ASWebAuthenticationSession/SFSafariViewControlleron iOS, Chrome Custom Tabs on Android — not a raw embeddedWebView. Maya's 3DS and wallet pages frequently block embedded WebViews. - The
success/failure/cancelredirect URLs resolve to the webFRONTEND_URL. For apps, register those return paths as deep links / universal links (or detect the return URL inside the auth session and dismiss it) so the app regains control after payment. - The webhook is the source of truth. After the browser session closes, fetch the donation (
GET /api/v1/donations/{id}) to read the final status — don't infer success from the redirect alone.
Test Mode
Maya sandbox is available at https://pg-sandbox.paymaya.com. Use Maya's test card numbers for sandbox testing. Set MAYA_SANDBOX=true in .env.
| Scenario | Sandbox behaviour |
|---|---|
| Card payments | Testable with Maya sandbox card numbers |
| Webhooks | Fired from sandbox IPs 13.229.160.234, 3.1.199.75 |