Pay U IOS SDK 3.2 Seamless Integration 提示缺少 FURL 参数
结论
这不是 Info.plist 的配置问题,也不是 simulator 导致的。这个错误说明 PayU 最终收到的支付 POST 请求中没有有效的 furl 字段。
代码里虽然设置了 FURL,但还要确认 SDK 确实把它序列化到了 postParam 中。同时,也要检查 PayUCreateRequest 返回的 NSMutableURLRequest,确认加载支付页面时没有重新创建请求,或把 POST 请求转换成 GET 请求。
常见原因包括:
- 示例代码与项目实际使用的 SDK 版本不一致;
- 项目中同时存在不同版本的 PayU 头文件或静态库;
PayUCreateRequest返回的 POST 请求被PayUMoneyViewController改写;- hash、transaction ID 等参数仍使用示例中的固定值,导致后续校验异常。
先检查生成的 POST 参数
在 completion block 中直接打印 postParam 和请求体:
[self.createRequest
createRequestWithPaymentParam:self.paymentParamForPassing
forPaymentType:PAYMENT_PG_CCDC
withCompletionBlock:^(NSMutableURLRequest *request,
NSString *postParam,
NSString *error) {
NSLog(@"Error: %@", error);
NSLog(@"PostParam: %@", postParam);
NSLog(@"HTTP Method: %@", request.HTTPMethod);
NSString *httpBody =
[[NSString alloc] initWithData:request.HTTPBody
encoding:NSUTF8StringEncoding];
NSLog(@"HTTP Body: %@", httpBody);
}];
正常情况下,postParam 或 HTTPBody 中应该能看到类似内容:
surl=https%3A%2F%2Fpayu.herokuapp.com%2Fios_success
&furl=https%3A%2F%2Fpayu.herokuapp.com%2Fios_failure
如果里面没有 furl=,问题出在 PayUCreateRequest 生成请求之前。如果已经有 furl=,但 PayU 仍提示缺失,就说明请求在加载 WebView 时发生了变化。
检查 SDK 的属性名称和版本
Objective-C 的属性名称区分大小写。请以当前项目实际引入的 PayUModelPaymentParams.h 为准,不要只照搬其他版本的示例。
如果头文件声明如下:
@property (nonatomic, strong) NSString *SURL;
@property (nonatomic, strong) NSString *FURL;
代码就应该写成:
self.paymentParamForPassing.SURL =
@"https://payu.herokuapp.com/ios_success";
self.paymentParamForPassing.FURL =
@"https://payu.herokuapp.com/ios_failure";
如果当前 SDK 头文件声明的是小写 surl 和 furl,代码也必须使用小写属性。项目中不要同时保留不同版本的 PayU SDK、头文件或静态库,否则可能出现编译时使用一套接口,运行时却加载另一套实现的情况。
创建请求前可以直接验证属性值:
NSAssert(self.paymentParamForPassing.SURL.length > 0, @"SURL is empty");
NSAssert(self.paymentParamForPassing.FURL.length > 0, @"FURL is empty");
NSLog(@"SURL: %@", self.paymentParamForPassing.SURL);
NSLog(@"FURL: %@", self.paymentParamForPassing.FURL);
不要重新构造返回的请求
createRequestWithPaymentParam: 返回的请求已经包含 POST body,后续应该直接加载这个 request:
- (void)viewDidLoad {
[super viewDidLoad];
NSAssert(self.theRequest != nil, @"PayU request is nil");
NSLog(@"Method: %@", self.theRequest.HTTPMethod);
[self.webView loadRequest:self.theRequest];
}
不要只取出 URL,再创建一个新请求:
// 错误示例:POST body 会丢失
NSURLRequest *newRequest =
[NSURLRequest requestWithURL:self.theRequest.URL];
[self.webView loadRequest:newRequest];
这种写法会把原来的 POST 请求变成没有请求体的普通请求,furl、surl、hash 等参数也会随之丢失。
整理后的参数代码
PayUModelPaymentParams *params = [PayUModelPaymentParams new];
params.key = @"YOUR_KEY";
params.transactionID = randomString;
params.amount = @"1";
params.productInfo = @"Nokia";
params.firstName = @"Ram";
params.email = @"email@example.com";
params.phoneNumber = @"1111111111";
params.SURL = @"https://your-domain.example/payu/success";
params.FURL = @"https://your-domain.example/payu/failure";
params.udf1 = @"u1";
params.udf2 = @"u2";
params.udf3 = @"u3";
params.udf4 = @"u4";
params.udf5 = @"u5";
params.environment = ENVIRONMENT_MOBILETEST;
params.expiryYear = @"2019";
params.expiryMonth = @"12";
params.nameOnCard = @"NOC";
params.cardNumber = @"5123456789012346";
params.CVV = @"123";
self.paymentParamForPassing = params;
原代码中的这行使用了弯引号:
self.paymentParamForPassing.nameOnCard = @“NOC”;
实际代码必须使用英文半角双引号:
self.paymentParamForPassing.nameOnCard = @"NOC";
hash 和交易号也要修正
示例中的 hash 不能直接复制并长期使用。支付 hash 通常与 key、transactionID、amount、productInfo、用户信息及其他参数有关。只要其中一个字段发生变化,就必须根据当前参数重新生成 hash。
代码虽然生成了 randomString,实际使用的交易号却仍是固定值:
self.paymentParamForPassing.transactionID = @"Ywism0Q9XC88qvy";
应改为:
self.paymentParamForPassing.transactionID = randomString;
每笔支付都应使用唯一的 transaction ID,并为这笔交易重新生成对应的 hash。生产环境中不要把 salt 放进 App,也不要在客户端生成依赖 salt 的支付 hash。salt 应保存在服务端,由服务端生成 hash 后返回给 App。
关于 Info.plist
在 Xcode 6.3 和 iOS 8.3 下,通常不需要为 surl 或 furl 添加 Info.plist 配置。App Transport Security 从 iOS 9 才开始引入,因此这个错误不是 ATS 配置引起的。
可以按以下顺序排查:
- 查看当前 SDK 头文件,确认属性名是
FURL还是furl。 - 打印
postParam,确认其中存在非空的furl=。 - 确认返回的请求使用 POST,并且
HTTPBody没有丢失。 - 在支付控制器中直接加载 SDK 返回的请求。
- 移除重复或版本不同的 PayU SDK 文件。
- 使用唯一的 transaction ID,并根据真实参数重新生成 hash。
如果 postParam 中没有 furl=,重点检查 SDK 版本和参数序列化。如果 postParam 中已经有 furl=,则需要检查 WebView 加载请求时是否丢失了 POST body。
备注:内容仅供参考。
