简介
小游戏服务端接入 mPaaS 内容魔方,需分别接入 查询订单状态 和 订单状态回调 两个接口。
订单状态回调
为确保服务端能及时接收支付结果,请按照下述规范接入 订单状态回调 接口。当用户支付成功后,系统将主动调用该接口通知商户后端。
若回调失败,系统将在 24 小时内进行 3~10 次重试。
由于网络波动等不可控因素,回调无法保证 100% 触达。强烈建议商户后端在收到回调后,或作为兜底方案,主动调用 查询订单状态 接口以确认最终订单状态。
查询订单状态
此为服务端主动发起的查询接口,用于弥补回调可能丢失的情况,确保支付状态最终一致。
接入前提
拥有阿里云账号并开通 mPaaS 相关权限。
在服务端集成对应的 SDK。
最佳实践
建议商户后端采用定时轮询或在未收到回调时触发查询,以主动获取订单最终状态,保障业务稳定性。
账号前置准备
业务接入确认
若当前仅考虑接入 订单状态回调,请直接参见 服务端接口 说明部分,忽略后续步骤。
若需要接入 查询订单状态,请继续参考后续步骤进行 AccessKeyId/AccessKeySecret 配置。
RAM 用户创建
请确保您已创建阿里云账号,并成功登录阿里云 RAM 控制台,身份管理 > 用户 > 创建用户,进行用户创建,必须勾选 使用永久 AccessKey 访问。

AK/SK 保存
创建完成后,仅首次创建,会出现 AccessKey 和 AccessSecret 相关信息,务必复制保存!

申请 mPaaS 权限
在阿里云 RAM 控制台,权限管理 > 授权,授权主题选择刚创建的 RAM 用户,权限策略选择 AliyunMPAASFullAccess,新增 mPaaS 接入权限。

AK/SK 使用
目前仅查询订单状态,需主动接入 SDK 依赖后,使用 AK/SK,访问 mPaaS 服务进行订单状态主动查询。
说明整体详细流程可参考 对 RAM 账号进行应用级别的访问控制,在 创建 AccessKey(请前往阿里云工作台进行创建)中可查看 AccessKeyId 与 AccessKeySecret 的具体含义及使用方式 。
服务端接口
订单状态回调
接口说明
游戏商服务端需主动实现该 HTTP 接口,供 mPaaS 调用以接收订单状态变更通知。接口定义包含路径、请求方式、参数及返回结果。
项目 | 说明 |
请求方式 | POST |
接口路径 |
|
Content-Type | application/x-www-form-urlencoded |
超时时间 | 5 秒 |
签名位置 | URL 参数 |
入参
参数 | 类型 | 是否必选 | 示例 | 描述 |
custom_id | String | 是 | ORDER_20260408_001 | CP 订单号,CP 侧唯一标识。 |
trade_no | String | 是 | 2026040800123456 | 业务订单号,平台侧订单号。 |
open_id | String | 是 | 8921198212819 | 开放平台用户 ID。 |
cp_extra | String | 是 | {"order_status":"ORDER_SUCCESS"} | 扩展参数,JSON 格式,包含 order_status 等业务信息。 |
sign | String | 否 | A1B2C3D4E5F6... | 签名值,拼接在 URL 参数中,详见 签名算法。 |
cp_extra 扩展字段说明
字段 | 类型 | 是否必选 | 示例 | 描述 |
order_status | String | 是 | ORDER_SUCCESS | 订单状态:
|
amount | Integer | 是 | 100 | 金额(单位:分) |
出参
参数 | 类型 | 是否必选 | 示例 | 描述 |
response | Object | 是 | 见下方 示例。 | 响应体,包含 code 和 msg。 |
response.code | String | 是 | 10000 | 结果码 |
response.msg | String | 是 | Success | 结果信息 |
response.sub_code | String | 否 | SYSTEM_ERROR | 业务错误码(失败时必选) |
response.sub_msg | String | 否 | 系统繁忙 | 业务错误描述(失败时必选) |
sign | String | 是 | A1B2C3D4E5F6... | 签名值 |
response.code 结果码说明
结果码 | 结果信息 | 描述 |
10000 | Success | 处理成功 |
40004 | Business Failed | 业务处理失败 |
错误码只支持 10000(成功)和 40004(失败),其他值会被视为非法响应。
sub_code 和 sub_msg 为可选参数,仅在业务失败时返回,且值不能为空。
CP 需在收到通知后 5 秒内 返回响应,否则视为通知失败。
返回
code=10000表示处理成功,平台不再发送通知。返回
code=40004或超时未响应,平台会进行重试。
出参示例
成功
{
"response": {
"code": "10000",
"msg": "Success"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}失败
{
"response": {
"code": "40004",
"msg": "Business Failed",
"sub_code": "SYSTEM_ERROR",
"sub_msg": "系统繁忙"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}查询订单状态
请优先完成 SDK 接入,具体步骤请参考 SDK 调用示例 - SDK 依赖 章节。
代码示例仅用于展示参数结构,不建议直接集成到业务代码中。
接口说明
面向游戏服务端,在 订单创建 且 支付成功 后,需主动调用此接口以核实用户订单状态。
入参
QueryPayOrderToMsenceRequest | |||
参数 | 类型 | 是否必选 | 描述 |
miniProgramId | String | Y | 小程序 ID |
platformId | String | Y | 平台 ID,例如:
|
customId | String | Y | 客户订单号 |
出参
参数 | 类型 | 描述 |
orderStatus | String |
|
requestId | String | 公共出参,请求 ID |
success | boolean | 公共出参,是否请求成功 |
resultCode | String | 公共出参,结果码 |
resultMsg | String | 公共出参,结果信息 |
resultCode 结果码说明
结果码 | 结果信息 | 备注 |
200 | SUCCESS | 成功 |
1001 | ILLEGAL_ARGUMENTS | 参数错误 |
1002 | MINI_GAME_UNREGISTER | 小程序游戏未注册 |
1003 | ORDER_NOT_EXIST | 订单不存在 |
1004 | REGISTER_INFO_INCOMPLETE | 小程序注册信息不完整 |
1005 | RPC_EXCEPTION | 调用第三方系统异常 |
1006 | RPC_RESULT_FAIL | 调用第三方服务结果失败 |
9999 | SYSTEM_EXCEPTION | 系统异常 |
出参示例
{
"RequestId": "2240a4ee90a64ad1a61cf64676861a08",
"Success": true,
"ResultCode": "200",
"ResultMsg": "SUCCESS",
"MpaasUserGamecenterPaymentQuerystatusResponse": {
"orderStatus": "1"
}
}签名算法
为确保链路的安全性与可靠性,mPaaS 在调用订单状态回调时会对参数进行签名。商家服务端需对回调参数执行签名验证。下文将详细介绍 mPaaS 的签名机制及验证方案。
签名规则
采用 MD5 签名,与支付宝签名算法保持一致。签名结果拼接到 URL 参数中。
步骤 | 说明 |
步骤 1 | 过滤空值参数和 |
步骤 2 | 将剩余参数按字母升序排列。 |
步骤 3 | 拼接成 |
步骤 4 | 在末尾拼接密钥: |
步骤 5 | 进行 MD5 加密,转大写。 |
签名示例
原始参数
custom_id=ORDER_20260408_001
trade_no=2026040800123456
open_id=8921198212819
cp_extra={"order_status":"ORDER_SUCCESS"}排序后拼接
cp_extra={"order_status":"ORDER_SUCCESS"}&custom_id=ORDER_20260408_001&open_id=8921198212819&trade_no=2026040800123456拼接密钥(假设密钥为 abc123)
cp_extra={"order_status":"ORDER_SUCCESS"}&custom_id=ORDER_20260408_001&open_id=8921198212819&trade_no=2026040800123456&key=abc123MD5 加密结果
A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6回调 URL 示例
POST /notify/order/status?sign=A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6
Content-Type: application/x-www-form-urlencoded
custom_id=ORDER_20260408_001&trade_no=2026040800123456&open_id=8921198212819&cp_extra=%7B%22order_status%22%3A%22ORDER_SUCCESS%22%7D签名工具类示例
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* 通知签名工具类
* @author chenweiyue.cwy
* @version $Id: NotifySignUtil.java, v 0.1 2026-04-09 10:00 chenweiyue.cwy Exp $$
*/
public class NotifySignUtil {
/**
* 生成签名
* @param params 参数Map
* @param secret 签名密钥
* @return 签名值
*/
public static String generateSign(Map<String, String> params, String secret) {
if (params == null || params.isEmpty() || StringUtils.isBlank(secret)) {
return null;
}
// 1. 过滤空值并排序
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
// 2. 拼接字符串
StringBuilder sb = new StringBuilder();
for (String key : keys) {
String value = params.get(key);
if (StringUtils.isNotBlank(value) && !"sign".equals(key)) {
sb.append(key).append("=").append(value).append("&");
}
}
sb.append("key=").append(secret);
// 3. MD5加密,转大写
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
/**
* 验证签名
* @param params 参数Map
* @param sign 签名值
* @param secret 签名密钥
* @return 验签是否通过
*/
public static boolean verifySign(Map<String, String> params, String sign, String secret) {
if (StringUtils.isBlank(sign) || StringUtils.isBlank(secret)) {
return false;
}
String calculatedSign = generateSign(params, secret);
return sign.equals(calculatedSign);
}
/**
* 从请求参数中构建签名Map(排除sign参数)
* @param params 原始参数Map
* @return 用于签名的Map
*/
public static Map<String, String> buildSignParams(Map<String, String[]> params) {
Map<String, String> signParams = new HashMap<>();
if (params == null || params.isEmpty()) {
return signParams;
}
for (Map.Entry<String, String[]> entry : params.entrySet()) {
String key = entry.getKey();
if ("sign".equals(key)) {
continue;
}
String[] values = entry.getValue();
if (values != null && values.length > 0) {
StringBuilder valueStr = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (i > 0) {
valueStr.append(",");
}
valueStr.append(values[i]);
}
signParams.put(key, valueStr.toString());
}
}
return signParams;
}
/**
* 生成响应签名
* @param responseMap 响应内容Map
* @param secret 签名密钥
* @return 签名值
*/
public static String generateResponseSign(Map<String, Object> responseMap, String secret) {
if (responseMap == null || responseMap.isEmpty() || StringUtils.isBlank(secret)) {
return null;
}
// 将Object值转换为String
Map<String, String> stringParams = new HashMap<>();
for (Map.Entry<String, Object> entry : responseMap.entrySet()) {
if (entry.getValue() != null) {
stringParams.put(entry.getKey(), String.valueOf(entry.getValue()));
}
}
return generateSign(stringParams, secret);
}
}SDK 调用示例
SDK 依赖
Java
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>mpaas20201028</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.3.10</version>
</dependency>Go
go get github.com/alibabacloud-go/mpaas-20201028/v3PHP
composer require alibabacloud/mpaas-20201028代码示例
示例代码仅供参考,请勿直接用于生产环境接入。如有任何疑问,欢迎搜索群号 145930007362 加入钉钉群进行咨询交流。
Java
public static String REGION_ID = "cn-hangzhou";
public static String ENDPOINT = "mpaas.cn-hangzhou.aliyuncs.com";
// 杭州非金预发
public static String ACCESS_KEY_ID = "AK";
public static String ACCESS_KEY_SECRET = "AS";
private static String MINI_PROGRAM_ID = "小程序ID";
private static String PLATFORM_ID = "平台ID";
public static String CUSTOM_ID = "订单自定义ID";
public static void main(String[] args) throws Exception {
Config config = new Config();
// 必填,您的 AccessKey ID
config.setAccessKeyId(MPAAS_AK_ENV);
// 必填,您的 AccessKey Secret
config.setAccessKeySecret(MPAAS_SK_ENV);
// mPaaS 的 REGION_ID 和 Endpoint,以杭州非金为例
config.setRegionId(REGION_ID);
config.setEndpoint(ENDPOINT);
Client client = new Client(config);
// 调用保存订单关系信息接口
saveOrderRelationinfo(client);
}
private static void queryPayOrderToMsence(Client client) throws Exception {
QueryPayOrderToMsenceRequest request = new QueryPayOrderToMsenceRequest();
request.setMiniProgramId(MINI_PROGRAM_ID);
request.setPlatformId(PLATFORM_ID);
request.setCustomId(CUSTOM_ID);
QueryPayOrderToMsenceResponse response = client.queryPayOrderToMsence(request);
System.out.println("response==>" + JSON.toJSONString(response));
}Go
package main
import (
"fmt"
"os"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
mpaas "github.com/alibabacloud-go/mpaas-20201028/v3/client"
"github.com/alibabacloud-go/tea/dara"
)
func main() {
if err := queryPayOrderToMsence(); err != nil {
fmt.Fprintf(os.Stderr, "查询订单失败: %v\n", err)
os.Exit(1)
}
}
// queryPayOrderToMsence 演示如何调用 QueryPayOrderToMsence 接口
// 该接口用于查询支付订单状态
func queryPayOrderToMsence() error {
// 1. 创建客户端配置
config := &openapi.Config{
// 阿里云 AccessKey ID
AccessKeyId: dara.String("LTAI5tMZR3xxx"),
// 阿里云 AccessKey Secret
AccessKeySecret: dara.String("C811bjRc8z9HxeI0Nxxx"),
// 区域 ID,例如:cn-hangzhou
RegionId: dara.String("cn-hangzhou"),
// 可选:自定义 endpoint
Endpoint: dara.String("mpaas.cn-hangzhou.aliyuncs.com"),
}
// 2. 创建客户端
client, err := mpaas.NewClient(config)
if err != nil {
return fmt.Errorf("创建 mpaas 客户端失败: %w", err)
}
// 3. 构建请求参数
request := &mpaas.QueryPayOrderToMsenceRequest{
// 平台标识
PlatformId: dara.String("mPaaS_Goosefish"),
// 小程序 ID
MiniProgramId: dara.String("123321"),
// 自定义 ID(与创建订单时传入的 CustomId 一致)
CustomId: dara.String("test_custom_id"),
}
// 4. 调用接口
response, err := client.QueryPayOrderToMsence(request)
if err != nil {
return fmt.Errorf("调用 QueryPayOrderToMsence 失败: %w", err)
}
// 5. 处理响应
if response.Body != nil {
fmt.Printf("请求 ID: %s\n", dara.StringValue(response.Body.RequestId))
fmt.Printf("是否成功: %v\n", dara.BoolValue(response.Body.Success))
fmt.Printf("结果码: %s\n", dara.StringValue(response.Body.ResultCode))
fmt.Printf("结果消息: %s\n", dara.StringValue(response.Body.ResultMsg))
if response.Body.MpaasUserGamecenterPaymentQuerystatusResponse != nil {
fmt.Printf("订单状态: %s\n", dara.StringValue(response.Body.MpaasUserGamecenterPaymentQuerystatusResponse.OrderStatus))
}
}
return nil
}PHP
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\SDK\MPaaS\V20201028\MPaaS;
use AlibabaCloud\SDK\MPaaS\V20201028\Models\QueryPayOrderToMsenceRequest;
use AlibabaCloud\Dara\Exception\DaraException;
use AlibabaCloud\Dara\Models\RuntimeOptions;
use Darabonba\OpenApi\Models\Config;
// 1. 创建客户端配置
$config = new Config([
'accessKeyId' => getenv('ALIBABA_CLOUD_ACCESS_KEY_ID') ?: 'LTAI5tMZR33xxx',
'accessKeySecret' => getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET') ?: 'C811bjRc8z9HxeIxxx',
'regionId' => 'cn-hangzhou',
'endpoint' => 'mpaas.cn-hangzhou.aliyuncs.com',
]);
// 2. 创建客户端实例
$client = new MPaaS($config);
// 3. 构建请求参数
$request = new QueryPayOrderToMsenceRequest([
'customId' => 'your-custom-id', // 自定义ID
'miniProgramId' => 'your-mini-program-id', // 小程序ID
'platformId' => 'your-platform-id', // 平台ID
]);
// 4. 创建运行时配置(可选)
$runtime = new RuntimeOptions([
'readTimeout' => 10000,
'connectTimeout' => 5000,
]);
try {
// 方式一:使用默认运行时配置调用
$response = $client->queryPayOrderToMsence($request);
// 方式二:使用自定义运行时配置调用
// $response = $client->queryPayOrderToMsenceWithOptions($request, $runtime);
// 5. 处理响应
$body = $response->body;
echo "RequestId: " . $body->requestId . "\n";
echo "ResultCode: " . $body->resultCode . "\n";
echo "ResultMsg: " . $body->resultMsg . "\n";
echo "Success: " . ($body->success ? 'true' : 'false') . "\n";
// 获取订单状态
if ($body->mpaasUserGamecenterPaymentQuerystatusResponse) {
echo "OrderStatus: " . $body->mpaasUserGamecenterPaymentQuerystatusResponse->orderStatus . "\n";
}
} catch (DaraException $e) {
// 处理业务异常
echo "DaraException: " . $e->getMessage() . "\n";
echo "Code: " . $e->getCode() . "\n";
} catch (\Exception $e) {
// 处理其他异常
echo "Exception: " . $e->getMessage() . "\n";
}