PAYATHON 2026

Gcash payment 是否提供 API 集成?

支付小周

结论

GCash 可以用于应用支付,但一般不能直接调用面向所有开发者开放的“公共 GCash API”。

通常有两种接入方式:

  1. 通过支持 GCash 的支付服务商或支付网关接入。
  2. 申请成为 GCash 商户,审核通过后使用其商户支付能力。

对于 React Native 应用,更可行的做法是选择支持 GCash 的支付网关。应用先请求自己的后端创建支付订单,再打开网关返回的结账页面或跳转链接。商户密钥不能保存在移动端,也不要从移动端直接调用需要密钥签名的支付接口。

推荐的接入结构

React Native App
       |
       | HTTPS
       v
Your Backend
       |
       | Merchant API
       v
Payment Gateway / GCash
       |
       | Webhook
       v
Your Backend

支付流程通常是:

  1. 用户在应用中提交订单。
  2. React Native 请求业务后端创建支付。
  3. 后端调用支付网关接口,并将 GCash 指定为支付方式。
  4. 网关返回结账 URL、支付会话 ID 或二维码数据。
  5. 应用打开结账页面,用户通过 GCash 完成付款。
  6. 支付网关通过 webhook 通知后端。
  7. 后端验证通知并更新订单状态。
  8. 应用重新查询订单,显示最终支付结果。

使用 WebView、系统浏览器、二维码还是 App 跳转,需要根据支付服务商的 SDK 和接口设计决定。

React Native 示例

以下代码只演示推荐的客户端调用方式。/api/payments/gcash 是应用自己的后端接口,不是 GCash 官方接口。

import React, { useState } from 'react';
import {
  Alert,
  Button,
  Linking,
  View,
} from 'react-native';

export default function CheckoutScreen() {
  const [loading, setLoading] = useState(false);

  const payWithGCash = async () => {
    try {
      setLoading(true);

      const response = await fetch(
        'https://api.example.com/api/payments/gcash',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            Authorization: 'Bearer YOUR_USER_ACCESS_TOKEN',
          },
          body: JSON.stringify({
            orderId: 'ORDER_12345',
          }),
        },
      );

      if (!response.ok) {
        throw new Error(`Create payment failed: ${response.status}`);
      }

      const data: {
        paymentId: string;
        checkoutUrl: string;
      } = await response.json();

      const supported = await Linking.canOpenURL(data.checkoutUrl);

      if (!supported) {
        throw new Error('Cannot open the payment page');
      }

      await Linking.openURL(data.checkoutUrl);
    } catch (error) {
      const message =
        error instanceof Error ? error.message : 'Unknown payment error';

      Alert.alert('Payment unavailable', message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View>
      <Button
        title={loading ? 'Creating payment...' : 'Pay with GCash'}
        disabled={loading}
        onPress={payWithGCash}
      />
    </View>
  );
}

支付金额不能由客户端直接决定。客户端只提交 orderId,后端再根据数据库中的订单计算应付金额,防止用户篡改请求。

后端示例

下面的 Express 示例使用占位网关地址和字段,仅用于说明客户端与后端的职责划分。实际 URL、认证方式、请求参数和响应格式应以所选支付服务商的文档为准。

import express from 'express';

const app = express();
app.use(express.json());

app.post('/api/payments/gcash', async (req, res) => {
  const { orderId } = req.body;

  // 应从数据库读取订单,并验证订单属于当前登录用户。
  const order = await findOrderById(orderId);

  if (!order || order.status !== 'pending') {
    return res.status(400).json({
      error: 'Invalid order',
    });
  }

  const gatewayResponse = await fetch(
    process.env.PAYMENT_GATEWAY_CREATE_URL,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.PAYMENT_GATEWAY_SECRET_KEY}`,
      },
      body: JSON.stringify({
        referenceId: order.id,
        amount: order.amount,
        currency: 'PHP',
        paymentMethod: 'GCASH',
        successUrl: 'myapp://payment/success',
        cancelUrl: 'myapp://payment/cancel',
      }),
    },
  );

  if (!gatewayResponse.ok) {
    return res.status(502).json({
      error: 'Payment provider rejected the request',
    });
  }

  const payment = await gatewayResponse.json();

  await savePayment({
    orderId: order.id,
    providerPaymentId: payment.id,
    status: 'pending',
  });

  return res.json({
    paymentId: payment.id,
    checkoutUrl: payment.checkoutUrl,
  });
});

paymentMethod、successUrl、checkoutUrl 等字段没有统一标准,不同服务商使用的名称可能完全不同。

Webhook 处理

不能只因为用户进入了成功页面,就把订单标记为已支付。用户可能中途关闭页面或伪造跳转 URL,付款结果也可能仍在处理中。最终支付状态应以后端查询结果或通过签名验证的 webhook 为准。

app.post('/api/webhooks/payment', async (req, res) => {
  const signature = req.headers['x-provider-signature'];

  const valid = verifyWebhookSignature(
    req.body,
    signature,
    process.env.PAYMENT_GATEWAY_WEBHOOK_SECRET,
  );

  if (!valid) {
    return res.sendStatus(401);
  }

  const event = req.body;

  if (event.type === 'payment.succeeded') {
    await markOrderAsPaid(event.data.referenceId);
  }

  if (event.type === 'payment.failed') {
    await markOrderAsFailed(event.data.referenceId);
  }

  return res.sendStatus(200);
});

签名请求头、签名算法和事件名称同样要按照实际支付服务商的文档实现。

接入前需要确认

选择支付服务商或申请商户能力时,需要确认以下事项:

  • 是否支持在菲律宾注册的个人或企业商户。
  • 是否明确支持 GCash,而不只是银行卡支付。
  • 是否提供测试环境。
  • 是否支持移动端浏览器跳转、App Link 或 Universal Link。
  • 交易费率、结算周期和退款费用。
  • 是否支持全额退款和部分退款。
  • webhook 是否带签名,发送失败后是否自动重试。
  • React Native WebView 能否打开支付页面。
  • 商户审核是否要求菲律宾公司、银行账户或相关身份材料。

GCash 的商户政策、开放范围和合作网关可能会发生变化。申请前应查看 GCash 商户渠道以及候选支付服务商的最新官方文档。

注意事项

  • 商户密钥只能保存在后端或安全的密钥管理系统中,不能写入 React Native 包。
  • 服务端必须重新读取订单金额、币种和商品信息。
  • 创建支付时应使用幂等键,避免用户重复点击后产生多个支付订单。
  • webhook 可能重复发送,因此订单状态更新也要支持幂等处理。
  • pending、paid、failed、expired 和 refunded 等状态应分别处理。
  • 返回 URL 只能改善用户体验,不能作为款项到账的判断依据。
  • 使用 WebView 前,应确认支付服务商是否允许。部分身份验证或外部 App 跳转可能无法在 WebView 中正常运行。
  • 普通 GCash 用户转账或个人二维码收款不能代替完整的应用支付 API。正式商用通常还需要订单关联、回调验签、退款和对账能力。

备注:内容仅供参考。