Integrating GCash and Maya: Payment Processing for the Philippine Market
Quick Answer: To integrate GCash and Maya, sign up for developer accounts with both providers, obtain API keys, build a unified payment abstraction layer in your backend, handle webhooks for real-time status updates, implement idempotency keys to prevent double-charging, and reconcile transactions daily against settlement reports. Both providers offer REST APIs with JSON responses, sandbox environments, and PHP-centric documentation, though the underlying protocols work with any modern stack.
Introduction
If you're building for the Philippine market, you already know the landscape: GCash dominates with over 80 million users, while Maya (formerly PayMaya) has carved out a strong position with roughly 30 million users and a growing suite of merchant tools. Together, they handle the lion's share of digital payments in the Philippines—far outpacing credit card adoption in a country where card penetration remains low.
But here's the thing most tutorials gloss over: integrating two different e-wallet providers isn't just about calling two APIs. It's about building a payment system that can handle inconsistent webhook timing, idempotent transaction states, reconciliation against daily settlement files, and the reality that many Filipinos switch between GCash and Maya depending on promotions or app availability.
This article walks through what we've learned building a unified payment layer for a Philippine marketplace. It's written in the spirit of building in public—sharing the patterns that worked, the traps that wasted weeks, and the production code we actually run.
Why GCash and Maya Together?
The Philippines is a mobile-first, cash-light economy where e-wallets function as de facto bank accounts for millions. GCash, backed by Alipay and local telco giant Globe, is the market leader with the widest agent network. Maya, backed by PLDT and now integrated with banking services, offers competitive merchant fees and a developer experience that some teams prefer.
Supporting both isn't a nice-to-have—it's table stakes. We've seen checkout conversion rates drop 15-20% when we only offered one provider, usually because the user's preferred wallet was either down for maintenance or they had funds in the other app. Offering both means covering nearly the entire addressable market of digitally paying consumers in the Philippines.
Getting Started: Developer Accounts and API Access
Before writing code, you need sandbox credentials from both providers. The process is similar but not identical.
GCash: Apply through the GCash Developer Portal. You'll need business registration documents (DTI or SEC), a live website or app, and a compliance review that typically takes 5-10 business days. Once approved, you get sandbox API keys for testing QR payments, direct debit, and the redirect-based checkout flow.
Maya: Maya's developer onboarding is faster—often 2-3 business days if your documents are complete. Their sandbox supports card payments, wallet payments, and QRPH (the interoperable QR standard). Maya also provides a handy webhook simulator that saved us hours of debugging.
One practical tip: create a shared .env structure early. We use separate prefixes to avoid key confusion in production:
GCASH_SANDBOX_BASE_URL=https://pgi.gcash.com/gcash/pg/v1
GCASH_PROD_BASE_URL=https://pgi.gcash.com/gcash/pg/v1
GCASH_MERCHANT_ID=your_merchant_id
GCASH_API_KEY=your_api_key
GCASH_WEBHOOK_SECRET=your_webhook_secret
MAYA_SANDBOX_BASE_URL=https://pg-sandbox.paymaya.com
MAYA_PROD_BASE_URL=https://pg.paymaya.com
MAYA_PUBLIC_API_KEY=pk-your_public_key
MAYA_SECRET_API_KEY=sk-your_secret_key
MAYA_WEBHOOK_SECRET=your_webhook_secret
Architecting a Unified Payment Layer
Rather than sprinkling GCash and Maya code throughout your application, build a single PaymentGateway abstraction. This is the pattern that saved our sanity when we added a third provider six months later.
Here's the interface we use:
interface PaymentGateway {
createPaymentIntent(amount: number, currency: string, metadata: object): Promise<PaymentIntent>;
verifyWebhookSignature(payload: string, signature: string): boolean;
parseWebhookPayload(payload: string): WebhookEvent;
getTransactionStatus(transactionId: string): Promise<TransactionStatus>;
}
Each provider implements this interface. The rest of your application—checkout flow, admin dashboard, refund logic—talks to the abstraction, not to GCash or Maya directly.
This matters because the two providers use different status codes. GCash returns SUCCESS, FAILURE, or PENDING. Maya returns PAYMENT_SUCCESS, PAYMENT_FAILED, PAYMENT_EXPIRED, or 3DS_REQUIRED. Your abstraction layer normalizes these into a unified enum before the rest of your system sees them.
Handling Webhooks: The Tricky Part
Webhooks are where most integrations stumble. Both GCash and Maya send asynchronous notifications to your endpoint when a payment succeeds, fails, or expires. But they differ in timing, retry logic, and payload structure.
GCash webhooks arrive within seconds of payment completion and retry up to 3 times with exponential backoff. Their payload includes a signature header signed with your webhook secret. Verify it.
Maya webhooks can occasionally arrive before the synchronous API response returns (race condition!). Maya retries for up to 24 hours and uses a different signature format—a base64-encoded HMAC-SHA256 of the raw payload.
Here's the defensive webhook handler we run in production:
app.post('/webhooks/gcash', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-gcash-signature'];
const payload = req.body;
if (!gcashGateway.verifyWebhookSignature(payload, signature)) {
return res.status(401).send('Invalid signature');
}
const event = gcashGateway.parseWebhookPayload(payload);
// Idempotency: have we already processed this transaction?
const existing = await db.transactions.findByProviderRef(event.providerRef);
if (existing && existing.processedAt) {
return res.status(200).send('Already processed');
}
await db.transactions.update(event.providerRef, {
status: event.normalizedStatus,
processedAt: new Date(),
rawPayload: payload
});
res.status(200).send('OK');
});
The critical line: always return 200 OK quickly, even if you're going to process asynchronously. Both providers will mark undelivered webhooks as failed and retry, potentially creating duplicate work. We queue webhook events to a background job processor (BullMQ on Redis) and respond immediately.
Reconciliation: Don't Skip This
Webhooks are great, but they're not authoritative. Network partitions, provider outages, and race conditions mean your database can drift from reality. We reconcile against provider settlement reports every morning at 6 AM Manila time.
GCash provides a daily settlement file via SFTP. Maya offers a similar report through their merchant dashboard, downloadable as CSV. Our reconciliation job:
- Downloads both reports
- Compares every transaction ID against our database
- Flags mismatches (e.g., webhook marked as SUCCESS but settlement shows FAILED)
- Alerts the finance team via Slack for manual review In three months of operation, reconciliation has caught four legitimate discrepancies—small amounts, but amounts that would have become accounting nightmares if left unchecked. One was a GCash transaction where the webhook succeeded but the settlement file showed a partial refund due to a user dispute initiated minutes after payment.
Sandbox Testing: Simulate the Real World
Both providers offer sandbox environments, but testing payment flows thoroughly requires going beyond happy-path scenarios.
GCash Sandbox: Use the test credentials to simulate a wallet with insufficient funds, an expired OTP, or a user-cancelled transaction. The sandbox doesn't always mirror production latency—production webhooks can take 5-10 seconds during peak hours, while sandbox is near-instant.
Maya Sandbox: Maya's simulator is more configurable. You can trigger specific error codes (e.g., 2125 for insufficient balance) by passing test card numbers or wallet IDs. This is invaluable for automated integration tests.
We run nightly CI jobs against both sandboxes using Playwright to simulate full checkout flows. It's caught breaking changes twice—once when Maya updated their redirect URL format without notice, and once when GCash changed their signature algorithm from SHA-1 to SHA-256.
Common Mistakes and How to Avoid Them
We've made every mistake below. Learn from our pain:
- Not validating webhook signatures: Early in our integration, we skipped signature verification for "speed." Within a week, a penetration test revealed that anyone could POST fake payment confirmations to our webhook endpoint. Always verify signatures.
- Assuming synchronous responses are final: Both GCash and Maya return a status from their create-payment API, but the real source of truth is the webhook. Treat synchronous responses as "payment initiated," not "payment complete."
- Hard-coding provider-specific logic in controllers: This creates technical debt fast. We refactored to the PaymentGateway abstraction after our first provider addition, and it paid for itself when we added a third.
- Ignoring PHP documentation: Both providers ship PHP SDKs as their primary documentation. If you're on Node.js, Python, or Go, you'll need to translate examples. The underlying REST APIs are straightforward, but the PHP-centric docs can be frustrating.
- Forgetting timezone handling: Settlement reports and webhook timestamps use Philippine Standard Time (UTC+8). Store everything in UTC internally, but convert for human-readable reports.
Security Considerations
Payment processing is a security-sensitive domain. Beyond webhook signature verification, implement these practices:
- Store API keys in a secrets manager (we use AWS Secrets Manager), never in environment variables on developer machines
- Rotate webhook secrets quarterly
- Use TLS 1.3 for all API communications
- Implement rate limiting on webhook endpoints to prevent abuse
- Log all payment events with non-reversible identifiers (hashed transaction IDs) for audit trails without exposing PII
Costs and Pricing
Pricing varies by merchant volume and negotiation, but as of 2025, expect roughly:
- GCash: 1.5-2.5% per transaction for QR payments; 2.5-3.5% for direct debit
- Maya: 1.8-2.8% per transaction for wallet payments; 2.8-3.8% for card payments Both charge monthly platform fees for enterprise accounts. Volume discounts kick in around PHP 1M in monthly transaction value. For startups, Maya's lower barrier to entry (faster approval, lower initial fees) can be attractive, while GCash offers broader consumer reach.
Key Takeaways
- Supporting both GCash and Maya is essential for Philippine market coverage—neither alone captures the full addressable market
- Build a unified
PaymentGatewayabstraction early to avoid provider-specific technical debt - Treat webhooks as the source of truth, not synchronous API responses, and always verify signatures
- Run daily reconciliation against settlement reports to catch discrepancies before they become accounting problems
- Test beyond happy paths in sandbox environments, including insufficient funds, expired sessions, and network failures
- Use idempotency keys to prevent double-charging when webhooks retry
- Plan for provider API changes—automated nightly CI against sandboxes catches breaking changes early
Frequently Asked Questions
Can I use GCash and Maya without a registered business in the Philippines?
No. Both providers require DTI or SEC registration, a live website or mobile app, and a compliance review. This is a regulatory requirement for payment aggregators in the Philippines under BSP (Bangko Sentral ng Pilipinas) guidelines.
Which provider has lower transaction fees?
Maya typically offers slightly lower rates for smaller merchants, while GCash provides volume discounts that become competitive at scale. Rates range from 1.5-3.5% depending on payment method and monthly transaction volume.
How long does integration take?
With prepared documentation, a simple redirect-based integration takes 1-2 weeks. Building a fully featured system with webhooks, reconciliation, refunds, and idempotency typically takes 4-6 weeks for an experienced team.
Do I need a separate merchant account for each provider?
Yes. GCash and Maya operate independently. You'll go through separate onboarding processes, receive separate settlement reports, and manage separate API keys and webhook endpoints.
What happens if a webhook fails to deliver?
GCash retries up to 3 times with exponential backoff. Maya retries for up to 24 hours. Both expect a 200 OK response. If your endpoint returns errors or timeouts, the provider may eventually mark the transaction as failed even if the user actually paid.
Can I process refunds automatically?
Yes, both providers support API-initiated refunds, but the process differs. GCash refunds are typically processed within 24-48 hours. Maya refunds can be instant for wallet payments but may take 5-10 business days for card transactions.
Is QRPH (unified QR) supported?
Yes, both GCash and Maya support QRPH, the interoperable QR standard mandated by the BSP. This allows consumers to scan a single QR code regardless of which app they use. Implementation requires additional compliance steps.
What programming languages are supported?
Official SDKs exist for PHP, but the underlying REST APIs work with any language that can make HTTPS requests. We've successfully integrated using Node.js, Python, and Go. Community SDKs exist for most major languages.
How do I handle disputed transactions?
Disputes are handled through each provider's merchant dashboard. GCash and Maya both notify merchants via email and dashboard alerts. Keep detailed transaction logs, user communication records, and delivery confirmations to support your case.
Can I test in production?
Neither provider recommends production testing with real money. Use the sandbox environments for all testing. Some merchants create small-value test transactions in production, but this risks real fees and complicates accounting.
Conclusion
Integrating GCash and Maya isn't trivial, but it's very achievable with the right architecture. The key is treating payment processing as infrastructure, not an afterthought—build the abstraction, verify the webhooks, reconcile the books, and test the edge cases.
If you're building for the Philippine market and navigating these integrations, we'd love to hear about your experience. Drop a comment below with the patterns that worked (or didn't) for your team.
Looking for help with your payment infrastructure? [Internal link: homepage] Our team specializes in marketplace integrations across Southeast Asia, and we've built unified payment layers for platforms processing millions in monthly volume. [Internal link: services overview] We also publish regular deep dives on Philippine fintech, e-commerce, and developer tooling. [Internal link: latest articles]









