iOS PayUMoney SDK 提示“key is not valid”
结论
最可能的问题是环境与凭据不匹配。代码使用了生产环境 PUMEnvironmentProduction,但传入的 key 不是当前生产商户账户的有效 Merchant Key。常见情况包括误用测试 Key、把 Merchant ID 当作 Merchant Key,或生产 Key 尚未启用。
Hash 能够正常计算,只代表 SHA-512 计算过程执行成功,不能证明该 Key 已在 PayUMoney 生产环境中注册并生效。服务端校验商户凭据时,仍可能返回 key is not valid。
排查和解决步骤
1. 确认环境与凭据匹配
当前代码使用生产环境:
self.params.environment = PUMEnvironmentProduction;
因此,以下信息必须来自同一个生产商户账户:
- Merchant Key
- Merchant Salt
- Merchant ID
- 已启用的生产账户配置
如果使用的是测试凭据,需要切换到 SDK 对应的测试环境。具体枚举名称取决于集成的 SDK 版本。例如,旧版 SDK 可能提供类似 PUMEnvironmentTest 的配置:
self.params.environment = PUMEnvironmentTest;
self.params.key = @"your_test_merchant_key";
self.params.merchantid = @"your_test_merchant_id";
测试 Key 不能用于生产环境,Merchant ID 也不能代替 Merchant Key。
如果 SDK 中没有 PUMEnvironmentTest,请查看当前版本头文件里 PUMEnvironment 的实际定义,不要直接套用其他版本的枚举名称。
2. 确保 Hash 使用同一组 Key 和 Salt
请求参数中的 key 必须与 Hash 字符串开头的 Key 完全一致:
self.params.key = merchantKey;
NSString *hashSequence = [NSString stringWithFormat:
@"%@|%@|%@|%@|%@|%@|||||||||||%@",
merchantKey,
self.params.txnid,
self.params.amount,
self.params.productinfo,
self.params.firstname,
self.params.email,
merchantSalt
];
可以用同一个变量传递 Key,避免请求参数和 Hash 使用不同的值:
- (void)setPaymentParameters {
NSString *merchantKey = @"your_merchant_key";
NSString *merchantSalt = @"your_merchant_salt";
self.params = [PUMRequestParams sharedParams];
self.params.environment = PUMEnvironmentProduction;
self.params.key = merchantKey;
self.params.merchantid = @"your_merchant_id";
// 其他参数……
self.params.hashValue = [self hashForParams:self.params
key:merchantKey
salt:merchantSalt];
}
3. 不要在客户端保存真实 Salt
示例代码直接在 iOS 应用中使用 Salt:
@"...|salt"
这种做法在生产环境中并不安全。iOS 应用可以被逆向分析,Salt 一旦被提取,攻击者就可能伪造请求 Hash。
生产环境应采用以下流程:
- iOS 客户端把订单信息发送到自己的后端。
- 后端生成唯一的
txnid。 - 后端使用 Merchant Key 和 Salt 计算 Hash。
- 后端把
txnid、Hash 和支付参数返回给客户端。 - 客户端启动 PayUMoney SDK。
生产 Salt 应只保存在服务端,不能打包进应用。
4. 检查商户账户状态
即使 Key 与环境看起来一致,生产 Key 仍可能因以下原因无法使用:
- 商户账户尚未完成激活或审核;
- 生产支付权限尚未开通;
- Key 属于另一个商户账户;
- 后台重新生成过凭据,但应用仍在使用旧 Key;
- Key 前后带有空格、换行或其他复制错误;
- 下载的旧 SDK 对应另一套 PayU/PayUMoney 接入配置。
请从实际商户后台重新复制生产 Merchant Key、Salt 和 Merchant ID,并确认三者属于同一个账户。不要使用文档中的演示值,也不要用 Merchant ID 代替 Merchant Key。
建议修正 Hash 的生成方式
当前代码通过 NSData 的 description 获取十六进制字符串:
NSString *rawHash = [[self createSHA512:hashSequence] description];
这种写法依赖 NSData 的描述格式,不适合正式编码。应直接把摘要字节转换成小写十六进制字符串:
#import <CommonCrypto/CommonDigest.h>
- (NSString *)sha512:(NSString *)source {
NSData *data = [source dataUsingEncoding:NSUTF8StringEncoding];
unsigned char digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
NSMutableString *result =
[NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
for (NSInteger i = 0; i < CC_SHA512_DIGEST_LENGTH; i++) {
[result appendFormat:@"%02x", digest[i]];
}
return result;
}
然后按照 PayUMoney 接口要求的字段顺序生成 Hash:
- (NSString *)hashForParams:(PUMRequestParams *)params
key:(NSString *)key
salt:(NSString *)salt {
NSString *hashSequence = [NSString stringWithFormat:
@"%@|%@|%@|%@|%@|%@|||||||||||%@",
key,
params.txnid,
params.amount,
params.productinfo,
params.firstname,
params.email,
salt
];
return [self sha512:hashSequence];
}
Hash 的字段数量、排列顺序和空字段必须严格符合所用 SDK 及接口版本的要求。上面的顺序与问题中的请求结构一致。如果当前商户后台提供了不同的集成文档,应以后台文档为准。
还需要修正的参数
当前 txnid 只有两位数字:
self.params.txnid = [self getRandomString:2];
这样的交易号很容易重复。它通常不会直接引发 key is not valid,但可能导致交易号重复或订单校验失败。建议由服务端生成足够长且唯一的交易号,例如:
self.params.txnid =
[[[NSUUID UUID] UUIDString] stringByReplacingOccurrencesOfString:@"-"
withString:@""];
另外,phone 被设置为空字符串:
self.params.phone = @"";
如果当前接口要求手机号必填,空字符串会导致后续参数校验失败。它不是 Key 无效的主要原因,但仍应根据所用 SDK 的要求填写有效值。
推荐的排查顺序
建议按以下顺序检查:
- 确认
PUMEnvironmentProduction使用的是生产 Merchant Key。 - 确认
self.params.key与 Hash 字符串中的 Key 完全相同。 - 确认 Salt 与 Key 来自同一个商户账户和同一环境。
- 确认
merchantid与 Merchant Key 没有填反。 - 确认生产账户已经激活,凭据也没有被重置。
- 核对 Hash 的字段顺序、空字段数量和字符编码是否符合当前接口文档。
- 使用长度合理且唯一的
txnid。 - 把 Hash 计算迁移到服务端。
所以,遇到这个提示时,应先检查生产环境与 Merchant Key 是否匹配,而不是只看 SHA-512 的计算结果。
备注:内容仅供参考。