最近项目业务需要用到ISV服务商模式,然后我也去做了一些了解,然后就开始准备调整原有的支付功能,迁移到isv模式。ISV模式大概的描述就不多说了,支付宝的文档还是挺详细的,在这中间遇到了一些问题,在这里记录一下。
APP支付
项目早期就已经集成了APP支付,我们在后端将订单信息进行一系列处理、签名之后,将信息通过接口返回给前端,前端直接使用这些信息去调用支付宝的SDK,这样的话前端不需要储存任何敏感信息。
public static string GetOrderInfoIsv(string title, string contents, string outTradeNo, string strMoney, string notifyUrl)
{
//公共参数部分
var payInfo = new Dictionary<string, string>
{
{"app_id", Config.AppIdSandBox},
{"biz_content", GetBizContentIsv(title, contents, outTradeNo, strMoney, "")},
{"charset", "utf-8"},
{"format", "json"},
{"method", "alipay.trade.app.pay"},
{"notify_url", notifyUrl},
{"sign_type", "RSA2"},
{"timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
{"version", "1.0"}
};
var strUrl = BuildQueryWithOutEncode(payInfo);
var sign = AlipaySignature.RSASign(strUrl, Config.PrivateKeySandBox, Config.Input_charset, Config.Sign_type, false);
payInfo.Add("sign", sign);
strUrl = GetOrderInfoWithEncode(payInfo);
strUrl = strUrl.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
return strUrl;
}
这里是原来的核心拼接逻辑,后端将得到的结果(也就是 strUrl 的内容)通过接口返回给前端,前端直接去调起支付。这个代码是没有问题的,但是在isv模式下,需要传入一个授权token,查阅文档后发现对应的参数是 app_auth_token ,然后将获取得token传入进去:
public static string GetOrderInfoIsv(string title, string contents, string outTradeNo, string strMoney, string notifyUrl)
{
var payInfo = new Dictionary<string, string>
{
{"app_id", Config.AppIdSandBox},
{"app_auth_token", "你的授权token"},
{"biz_content", GetBizContentIsv(title, contents, outTradeNo, strMoney, "")},
{"charset", "utf-8"},
{"format", "json"},
{"method", "alipay.trade.app.pay"},
{"notify_url", notifyUrl},
{"sign_type", "RSA2"},
{"timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
{"version", "1.0"}
};
var strUrl = BuildQueryWithOutEncode(payInfo);
var sign = AlipaySignature.RSASign(strUrl, Config.PrivateKeySandBox, Config.Input_charset, Config.Sign_type, false);
payInfo.Add("sign", sign);
strUrl = GetOrderInfoWithEncode(payInfo);
strUrl = strUrl.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
return strUrl;
}
添加了app_auth_token参数之后,如果你的token不正确是会弹出错误提示的,会提示你错误的授权令牌,你需要重新授权一下。
注意:如果你使用沙箱环境进行测试的话,一定要使用卖家账号进行授权才行。
然而在更换了正确的token之后,还是不能成功拉起支付,会报ALIN10146错误。通过使用支付宝开放平台工具,发现是签名不对应的问题,但是我又想不到有啥不对的,不加token就没问题,加了token就不行,脑壳疼。
最终解决方案
最后我放弃了我们这种有点古老的拼接方式,改成使用支付宝提供的 .net 服务端的SDK,这样我就不用去管签名的问题了,以下是Demo代码:
[HttpGet]
public object AppPay()
{
try
{
IAopClient client =new DefaultAopClient(AliPayConfig.AliDomainSandBox,
AliPayConfig.AppIdSandBox, AliPayConfig.PrivateKeySandBox, "json", "1.0", "RSA2",
AliPayConfig.PublicKeySandBox, "utf-8", false));
var param = new AppPayRequest
{
AccessParams = new AccessParams(),
ExtendParams = new ExtendParams()
{
StoreId = "100086",
SysServiceProviderId = "填写 账户中心-合作伙伴管理页面的 PID"
},
OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
Subject = "APP支付ISV",
TotalAmount = "100",
ProductCode = "QUICK_MSECURITY_PAY",
Body = "APP支付ISV",
TimeoutExpress = "30m"
};
var request = new AlipayTradeAppPayRequest();
request.SetNeedEncrypt(false);
request.SetNotifyUrl("支付回调在这里进行设置");
request.BizContent = JsonConvert.SerializeObject(param);
var response = client.SdkExecute(request, AliPayConfig.AppAuthTokenSandBox);
//注意这里调用的是 SdkExecute 方法,而非 Execute 方法,如果使用 Execute 方法会返回404
return response;
}
catch (Exception e)
{
return e;
}
}
//请求参数实体
public class AppPayRequest
{
[JsonProperty("total_amount")]
public string TotalAmount { get; set; }
[JsonProperty("subject")]
public string Subject { get; set; }
[JsonProperty("out_trade_no")]
public string OutTradeNo { get; set; }
[JsonProperty("goods_type")]
public string GoodsType => "1";
[JsonProperty("extend_params")]
public ExtendParams ExtendParams { get; set; }
[JsonProperty("access_params")]
public AccessParams AccessParams { get; set; }
public string ProductCode { get; set; }
public string Body { get; set; }
public string TimeoutExpress { get; set; }
public string SellerId { get; set; }
}
public class ExtendParams
{
[JsonProperty("sys_service_provider_id")]
public string SysServiceProviderId { get; set; }
[JsonProperty("store_id")]
public string StoreId { get; set; }
}
public class AccessParams
{
[JsonProperty("channel")]
public string Channel => "ALIPAYAPP";
}
手机网站支付
在APP支付使用SDK之后,手机网站支付就变得非常简单了,以下是demo代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using AliPayISV.Entities;
using Aop.Api;
using Aop.Api.Request;
using Newtonsoft.Json;
namespace AliPayISV.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
try
{
IAopClient client =new DefaultAopClient(AliPayConfig.AliDomainSandBox,
AliPayConfig.AppIdSandBox, AliPayConfig.PrivateKeySandBox, "json", "1.0", "RSA2",
AliPayConfig.PublicKeySandBox, "utf-8", false));
var param = new AppPayRequest
{
AccessParams = new AccessParams(),
ExtendParams = new ExtendParams()
{
StoreId = "100086",
SysServiceProviderId = "服务商的PID"
},
OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
Subject = "APP支付ISV",
TotalAmount = "100",
ProductCode = "QUICK_MSECURITY_PAY",
Body = "APP支付ISV",
TimeoutExpress = "30m"
};
var request = new AlipayTradeWapPayRequest();
request.SetReturnUrl("返回的页面");
request.SetNotifyUrl("回调页面");
request.BizContent = JsonConvert.SerializeObject(param);
var response = client.pageExecute(request, null, AliPayConfig.AppAuthTokenSandBox);
//注意这里调用的是 pageExecute 方法
return Content(response.Body);
}
catch (Exception e)
{
return Content(e.Message);
}
}
}
}
当面付
当面付就更简单了,这个当初也是我开发的,所以一直使用的都是SDK,demo代码如下:
[HttpGet]
public object AliF2FPay()
{
try
{
var client = new DefaultAopClient(AliPayConfig.AliDomainSandBox,
AliPayConfig.AppIdSandBox, AliPayConfig.PrivateKeySandBox, "json", "1.0", "RSA2",
AliPayConfig.PublicKeySandBox, "utf-8", false));
var request = new AlipayTradePrecreateRequest();
var param = new AppPayRequest()
{
OutTradeNo = DateTime.Now.ToString("yyyyMMddHHmmssfff"),
Subject = "当面付测试",
TotalAmount = "1000",
ExtendParams = new ExtendParams()
{
StoreId = "10086",
SysServiceProviderId = "服务商的pid" //合作伙伴中的pid
}
};
request.SetNotifyUrl(AliPayConfig.AuthNotifyLink);
request.BizContent = JsonConvert.SerializeObject(param);
var response = client.Execute(request, null, AliPayConfig.AppAuthTokenSandBox);
return response;
}
catch (Exception e)
{
return e;
}
}
关键参数释疑
- sys_service_provider_id 填写服务商的PID,PID在下图位置
结语
文中所有的配置都是使用的沙箱环境进行的测试,也都已经调试通过了,由于子商户还没有申请下来,所以还没有正式的线上测试,不过想来问题应该不大,还有一些注意事项在支付宝的接入指南中已经说得很详细了,也就不再赘述了,放几个接入指南链接吧。如果读者(我感觉没人会读)接入时遇到什么问题,欢迎留言探讨。
* 支付宝ISV入驻以及返佣必读
* 第三方应用授权
Comments | NOTHING