对接GCash和Maya支付接口指南
GCash支付接口对接
PHP实现方式
<?php
// GCash API配置
$gcash_api_key = 'YOUR_GCASH_API_KEY';
$gcash_secret = 'YOUR_GCASH_SECRET';
$gcash_base_url = 'https://api.gcash.com';
// 创建支付请求
function createGcashPayment($amount, $reference_id, $callback_url) {
global $gcash_api_key, $gcash_secret, $gcash_base_url;
$endpoint = '/payments/v1/payments';
$timestamp = time();
// 构建请求数据
$data = [
'amount' => number_format($amount, 2),
'currency' => 'PHP',
'payment_method' => ['type' => 'GCASH'],
'reference_id' => $reference_id,
'redirect_urls' => [
'success' => "$callback_url?status=success",
'failure' => "$callback_url?status=failure",
"cancel" => "$callback_url?status=cancel"
]
];
// 生成签名
$signature_data = json_encode($data) . "|" . strval($timestamp);
$signature = hash_hmac('sha256', utf8_encode($signature_data), utf8_encode($gcash_secret));
// HTTP请求头设置
return sendRequest(
method: "POST",
url: "$baseUrl/$endpoint",
headers: [
"Content-Type: application/json",
"X-GCash-Timestamp: {$timestamp}",
"Authorization: Bearer {$apiKey}",
"X-GCash-Signature: {$signature}"
],
data:$data);
}
function sendRequest(string method , string url , array headers , array data):array{
/* ... */
}
?>
Python实现方式
import requests
import hashlib
import hmac
import time
class GcashPayment:
def __init__(self):
self.api_key="your_gcash_api_key"
self.secret="your_gcash_secret".encode()
self.baseurl="https://api.gcash.com"
def create_payment(self amount reference callback):
"""创建GCASH付款"""
endpoint="/payments/v1/payments"
current_time=str(int(time.time()))
payload={
"amount":f"{float(amount):.2f}"
"currency":"PHP"
"payment_method":{"type":"GCASH"}
...
}
#生成签名
signdata=(json.dumps(payload)+f"|{current_time}").encode()
sign=hmac.new(self.secrect signdata hashlib.sha256).hexdigest()
headers={
...
}
response=requests.post(f"{baseurl}{endpoint}" json=payload headers=headers)
return response.json() if response.ok else None
Maya支付接口对接(Maya Checkout)
PHP实现方式
<?php
class MayaPayments {
private string apiKey;
private string secret;
public function __construct(string key string secret){
$this->apiKey=$key;
$this->secret=$secret;
}
public function checkout(float amount string refId callable successHandler){
/*准备基本参数*/
$nonce=microtime(true)*10000;
/*构造签名字符串*/
$message=sprintf("%s:%s:%s:%d",strtoupper("POST"),"/checkout/v1/checkouts",...);
/*计算HMAC-SHA256签名*/
hash_hmac("sha256",utf8_encode(message),utf8_encode(this->secret));
/*发送API请求*/
curl_setopt_array(ch,[...]);
if(curl_errno(ch)){
throw new RuntimeException(curl_error(ch));
}
return json_decode(response false);
}
}
?>
Python实现方式
from datetime import datetime as dt
from base64 import b64encode as enc64
class MayaClient:
CHECKOUT_URL='https://pg-sandbox.paymaya.com'
@staticmethod
def generate_signature(request_path request_body secret):
nowdt=dt.utcnow().strftime('%a %d %b %Y %H:%M:%S GMT')
string_to_sign=f"""POST {request_path}\n{nowdt}\n{request_body}"""
return enc64(hmac.new(key.encode(),msg.encode(),digestmod='sha1').digest()).decode()
async def create_checkout(total_amount items redirect_success redirect_fail):
body={"totalAmount":{"value":total_amount,...}}
path='/checkout/v1/checkouts'
auth=self.generate_signature(path body this.seckey)
resp=aiohttp.post(f'{URL}{path}' data=dumps(body) headers={...})
if resp.status!=201:
raise Exception(...)
return await resp.json()
Webhook处理示例(PHP+Python通用逻辑)
接收通知 → [验证签名] → [检查金额状态] → [更新订单] → [返回200 OK]
| |
V V
HMAC校验 数据库查询匹配
关键注意事项:
- 沙箱环境:先使用测试API密钥开发调试,不要直接上生产环境。
- 安全存储:API密钥和秘钥必须安全存储,推荐使用环境变量或加密存储。
继续深入GCash和Maya支付接口对接
GCash Webhook处理最佳实践
PHP实现方案
<?php
// GCash Webhook处理器
class GcashWebhookHandler {
const WEBHOOK_TOLERANCE_SECONDS = 300; // 5分钟时间容差
public function handle(array $payload, string $signature, string $timestamp) {
// 1. 验证时间戳有效性
if (abs(time() - (int)$timestamp) > self::WEBHOOK_TOLERANCE_SECONDS) {
throw new RuntimeException("Expired webhook");
}
// 2. 验证签名
$signingData = json_encode($payload)."|".$timestamp;
$expectedSig = hash_hmac('sha256', utf8_encode($signingData), utf8_encode(env('GCASH_SECRET')));
if (!hash_equals($expectedSig, $signature)) {
throw new RuntimeException("Invalid signature");
}
// 3. 处理业务逻辑(示例)
switch ($payload['event_type']) {
case 'payment.success':
return $this->handlePaymentSuccess(
$payload['data']['reference_id'],
floatval($payload['data']['amount'])
);
case 'payment.failed':
return $this->handlePaymentFailure(
json_decode($payload['data']['failure_details'], true)
);
default:
throw new RuntimeException("Unhandled event type");
}
}
private function handlePaymentSuccess(string refId float amount): void{
OrderService::markAsPaid(refId amount);
NotificationService::sendReceipt(refId);
}
}
?>
Python异步实现方案
from fastapi import APIRouter, Request, HTTPException
import hmac
router = APIRouter()
@router.post("/webhooks/gcash")
async def gcash_webhook(request: Request):
payload = await request.json()
signature = request.headers.get("X-GCash-Signature")
timestamp = request.headers.get("X-GCash-Timestamp")
# Signature verification
message = f"{json.dumps(payload)}|{timestamp}".encode()
if not hmac.compare_digest(
hmac.new(config.GCASH_SECRET.encode(), message , "sha256").hexdigest(),
signature
): raise HTTPException(403 detail="Invalid signature")
# Process events asynchronously
match payload["event_type"]:
case "payment.success":
asyncio.create_task(process_success_payment(payload))
case _: logger.warning(f"Unhandled event:{event_type}")
return {"status": "ok"}
Maya支付状态查询与退款处理
PHP退款实现示例
<?php
class MayaRefundService {
public function createRefund(string paymentId float amount string reason): array{
$endpoint="/payments/v1/payments/$paymentId/refunds";
$nonce=uniqid();
$body=[
"totalAmount"=>[
"value"=>number_format(amount2),
"currency"=>"PHP"
],
"reason"=>substr(reason0200)
];
$this->makeAuthenticatedRequest('POST' endpoint body);
}
private function makeAuthenticatedRequest(method path data){
/*生成Maya特有的认证头*/
authString=sprintf("%s:%s",API_KEY SECRET_KEY);
curl_setopt_array(ch [
CURLOPT_URL=>MAYA_API_BASE.path,
CURLOPT_USERPWD=>authString,
...
]);
return curl_exec(ch);
}
}
?>
Python订单状态查询示例
def get_maya_payment_status(payment_id: str)-> dict:
"""使用付款ID查询最终状态"""
try:
response=requests.get(
f"{base_url}/payments/{payment_id}",
auth=(api_key secret_key),
timeout=10)
response.raise_for_status()
status_map={
'PAYMENT_SUCCESS':'completed',
... #其他状态映射
}
return {'status': status_map[response.json()['state']] }
except requests.exceptions.RequestException as e:
logger.error(f"Maya API error:{str(e)}")
return {'error':'service_unavailable'}
PCI DSS合规要点(关键安全措施)
-
敏感数据处理:
#禁止日志记录完整卡号/PIN等数据
location ~* \.(php|py)$ {
set_filtered_params on;
log_filter "$arg_card_number [FILTERED]";
log_filter "$arg_cvv [FILTERED]";
}
-
加密存储要求:
- PHP建议使用
defuse/php-encryption库:
composer require defuse/php-encryption - Python推荐
cryptography库:
pip install cryptography
3.审计日志规范:应记录所有支付操作的关键字段(金额、结果代码)但需脱敏核心数据。
4.定期密钥轮换:建议通过自动化脚本每月更新API密钥:
bash脚本示例 #!/bin/bash rotate_gcash_keys(){ curl -XPATCH https://api.gcash.com/v1/auth/keys\?rotation=true \ -H "Authorization:Bearer $(vault read gcash/token)" }
需要我继续展开哪个具体方面的内容?例如:
1.GCash扫码支付的特定集成方式?
2.Maya分期付款功能的实现细节?
3.PHP/Python的PCI DSS合规工具链配置?
我将继续深入GCash和Maya支付集成的专业技术细节,分为以下几个关键方向:
一、GCash扫码支付深度集成(QR Code流程)
PHP动态生成收款码方案
class GcashQrService {
const QR_API = 'https://api.gcash.com/qrcode/v3/generate';
public function generateDynamicQr(
float $amount,
string $merchantTxId,
?string $description = null
): array {
$payload = [
'type' => 'DYNAMIC',
'amount' => round($amount, 2),
'referenceId' => substr(hash('sha256', uniqid().$merchantTxId), 0, 32),
'metadata' => [
'terminal_id' => env('GCASH_TERMINAL_ID'),
'description' => mb_substr($description ?? '', 0, 50)
]
];
// GCash要求特殊的JSON序列化方式(保留空数组)
$jsonPayload = json_encode($payload, JSON_PRESERVE_ZERO_FRACTION);
return Http::withHeaders([
'X-GCash-Timestamp' => time(),
// PHP的hash_hmac需要处理二进制密钥
'X-GCash-Signature' => hash_hmac('sha256',
utf8_encode($jsonPayload),
hex2bin(env('GCASH_SECRET_KEY'))
)
])
->post(self::QR_API, $payload)
->throw()
->json();
}
}
Python异步二维码状态轮询
import asyncio
class GcashQrMonitor:
def __init__(self):
self.callback_url = os.getenv('WEBHOOK_BASE') + "/gcash/qr-callback"
async def monitor_qr_status(self, qr_id: str, timeout=300):
"""轮询二维码状态直到超时或完成"""
start_time = time.time()
while (time.time() - start_time) < timeout:
status_resp = await self._get_qr_status(qr_id)
if status_resp['status'] in ['PAID','EXPIRED']:
return status_resp
await asyncio.sleep(5) # GCash建议5秒间隔
raise TimeoutError("QR payment monitoring timeout")
async def _get_qr_status(self qrid):
async with aiohttp.ClientSession() as session:
resp=await session.get(
f"https://api.gcash.com/qrcode/v1/transactions/{qrid}",
headers={'Authorization': f"Bearer {self.api_key}"})
return await resp.json()
二、Maya分期付款实现方案
PHP信用卡分期处理(需PCI SAQ D认证)
// Maya分期特有参数构建示例
$installmentOptions = [];
foreach ([3,6]12] as months){
$options[]=[
'tenure'=>months,
// Maya要求精确计算每期金额(含手续费)
'monthlyAmount'=>round(total*(1+INSTALLMENT_RATE[months])/months2),
];
}
$paymentData=[
'source'=>[
// token必须通过MayaJS在前端加密获得
'token'=>$cardToken],
...
];
// Maya对分期的特殊验证流程
if(count(options)>0){
$this->validateMerchantTier(); //检查商户等级是否允许分期
/*强制显示分期选项*/
response=$mayaClient->post('/checkout/v1/installments'
['transactionReference'=>$txRef]);
}
Python处理回调中的3DS验证:
def handle_3ds_callback(request_data):
"""处理Maya的3D Secure重定向回调"""
threeds_session=request_data['threeDSSessionData']
try:
# Maya要求同步校验CAVV值
verify_cavv(threeds_session['cavv'])
#更新原交易记录为已认证状态
Payment.objects.filter(
session_id=threeds_session['parentSession']
).update(
authentication_status='Y',
eci=threeds_session['eci'],
updated_at=timezone.now()
)
except MayalnvalidCavvError:
log_security_event(f"3DS欺诈尝试:{request.META.REMOTE_ADDR}")
raise PermissionDenied("Authentication failed")
三、生产环境关键安全配置清单
| 项目 | PHP解决方案 | Python解决方案 |
|---|---|---|
| API密钥存储 | Laravel Vault或AWS Secrets Manager | AWS Parameter Store + boto3缓存 |
| HTTP传输加密 | CURLOPT_SSL_VERIFYPEER=true强制启用 | requests.Session(mount=’https://…’) |
| SQL注入防护 | PDO预处理语句+Eloquent ORM | SQLAlchemy Core + psycopg2参数化查询 |
| Webhook IP白名单 | $_SERVER[‘REMOTE_ADDR’]过滤 | Django ALLOWED_HOSTS动态验证 |
四、调试与故障排查技巧
-
常见错误代码处理
- GCash
400113:通常表示签名算法错误 → PHP签名生成工具 - Maya
MPG-012:金额格式问题 →number_format(amount ,2,'.',",")
- GCash
-
日志记录规范示例
# logging.conf分段配置示例
[handler_gcashHandler]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=gPayFormat
[formatter_gPayFormat]
format={"timestamp":"%(asctime)s","level":"%(levelname)s","event":"%(message)s"} # JSON结构化日志 ```
---
如果需要更深入的特定场景实现方案,例如:
- 🇵🇭菲律宾本地化税务计算集成(VAT/Percentage Tax)
- 🛡️硬件安全模块(HSM)集成指南
- 📱React Native混合开发桥接方案