各服务端包中的可运行代码
各示例包含两种界面模式、通知处理、返回页、安全的状态接口及持久化的付款尝试记录。API 使用 notify_url 和 redirect_url;结账模式由本地接入选择,不是 Create Order 参数。
Node.js · 核实付款、返回与通知
以下节选自可运行示例包。接口变量和存储辅助函数在完整源码中定义,接入时请使用完整实现。
async function verify(r,t){const a=load(r);if(!equal(a.token,t)||!a.snapshot)throw Error('Invalid payment capability');const d=await api('/api/v1/orders/query','order_id='+encodeURIComponent(a.snapshot.order_id));bind(a,d);if(d.status==='PAID'&&!a.paid_verified){a.paid_verified=true;save(r,a)}return d}
// HPP redirect_url -> GET /complete: verify before displaying result.
await verify(r,t);
// notify_url -> POST /callback: same verification and atomic once-only marker.
const d=await verify(r,t);
output(res,d.status==='PAID'?200:409,'text/plain',d.status==='PAID'?'OK':'Payment not confirmed');
// ON_SITE polling -> GET /status: only the safe DTO reaches the browser.
json(res,200,{success:true,data:safe(await verify(r,t))});
Python · 核实付款、返回与通知
以下节选自可运行示例包。接口变量和存储辅助函数在完整源码中定义,接入时请使用完整实现。
def verify(r, t):
a = load(r)
if not isinstance(t,str) or not hmac.compare_digest(a['token'],t) or not a.get('snapshot'): raise ValueError('Invalid capability')
d = api('/api/v1/orders/query',urlencode({'order_id':a['snapshot']['order_id']})); bind(a,d)
if d.get('status') == 'PAID' and not a['paid_verified']: a['paid_verified'] = True; save(r,a)
return d
# HPP browser return
verify(r,t)
# HPP and ON_SITE server notification (inside the handler lock)
d = verify(r,t)
self.out(200 if d['status']=='PAID' else 409,'text/plain','OK' if d['status']=='PAID' else 'Payment not confirmed')
# ON_SITE display only
self.jout(200,dict(success=True,data=safe(verify(r,t))))
C# / .NET Framework · 核实付款、返回与通知
以下节选自可运行示例包。接口变量和存储辅助函数在完整源码中定义,接入时请使用完整实现。
static JObject Verify(string r,string t)
{
var a=Load(r);if(!Equal(S(a,"token"),t)||a["snapshot"]==null)throw new Exception("Invalid capability");var d=Api("/api/v1/orders/query",null,"order_id="+Uri.EscapeDataString(S(a["snapshot"],"order_id")));Bind(a,d);
if(S(d,"status")=="PAID"&&!(bool)a["paid_verified"]){a["paid_verified"]=true;Save(r,a);}return d;
}
// GET /complete: verified HPP return.
Verify(r,t);
// POST /callback: asynchronous notification for both modes.
var d=Verify(r,t);
Out(res,S(d,"status")=="PAID"?200:409,"text/plain",S(d,"status")=="PAID"?"OK":"Payment not confirmed");
// GET /status: display only the minimal DTO.
JOut(res,200,new JObject {{"success",true},{"data",Safe(Verify(r,t))}});
Java · 核实付款、返回与通知
以下节选自可运行示例包。接口变量和存储辅助函数在完整源码中定义,接入时请使用完整实现。
static JsonObject verify(String r,String t)throws Exception{JsonObject a=load(r);if(!equal(s(a,"token"),t)||!a.has("snapshot"))throw new IllegalArgumentException("Invalid capability");JsonObject d=api("/api/v1/orders/query",null,"order_id="+enc(s(a.getAsJsonObject("snapshot"),"order_id")));bind(a,d);if(s(d,"status").equals("PAID")&&!a.get("paid_verified").getAsBoolean()){a.addProperty("paid_verified",true);save(r,a);}return d;}
// GET /complete: verified HPP return.
verify(r,t);
// POST /callback: both presentation modes use this route.
boolean paid=s(verify(r,t),"status").equals("PAID");
out(x,paid?200:409,"text/plain",paid?"OK":"Payment not confirmed");
// GET /status: minimal safe display DTO.
jout(x,200,object("success",true,"data",safe(verify(r,t))));
PHP · 核实付款、返回与通知
以下节选自可运行示例包。接口变量和存储辅助函数在完整源码中定义,接入时请使用完整实现。
// Authenticate the result by querying MochiPay server-to-server.
$verified = mochipay_query_order('order_id', $orderId);
if (!$verified['ok'] || !is_array($verified['data'])) {
callback_text(503, 'VERIFICATION_FAILED');
}
$order = $verified['data'];
if (!isset($order['order_id']) || !hash_equals((string) $order['order_id'], $orderId)) {
callback_text(409, 'ORDER_MISMATCH');
}
$verifiedMerchantId = isset($order['merchant_order_id']) ? (string)$order['merchant_order_id'] : '';
try { $record = mochipay_load($verifiedMerchantId); }
catch (Exception $e) { callback_text(503, 'LOCAL_STORAGE_UNAVAILABLE'); }
if (!mochipay_bound($record, $order) || (isset($callback['merchant_order_id']) && !hash_equals($verifiedMerchantId, (string)$callback['merchant_order_id']))) callback_text(409, 'LOCAL_ORDER_MISMATCH');
if (!isset($order['received_amount']) || MochiPayPortable::decimal($order['received_amount']) !== MochiPayPortable::decimal($order['pay_amount'])) callback_text(409, 'PAYMENT_AMOUNT_REQUIRES_REVIEW');
if (!isset($order['status']) || strtoupper((string) $order['status']) !== 'PAID') {
callback_text(409, 'ORDER_NOT_PAID');
}
/*
* TODO: In your production database, atomically fulfill the bound local order.
* This demo acknowledges verification only; it does not deliver goods.
* Make the operation idempotent: repeated callbacks must not deliver goods
* or credit the customer more than once.
*/
try { mochipay_record_verified($record, $order); }
catch (Exception $e) { callback_text(503, 'LOCAL_UPDATE_FAILED'); }
callback_text(200, 'OK');
}
// GET browser return verifies the saved capability, then queries the saved ID.
$authorized = $record && $returnToken !== '' && hash_equals($record['token'], $returnToken);
if ($authorized) {
$verified = mochipay_query_order('order_id', $record['snapshot']['order_id']);
$paid = $verified['ok'] && mochipay_record_verified($record, $verified['data']);
}
下载完整示例