最近接手了一个做支付的外包项目,前台和后台都要做,所以打算就此几个笔记。
申请
在接通支付之前,我们还要做一些事情
创建应用
签约
这个签约部分需要实名认证的。我下面是签约完成的,添加你需要的功能,然后签约认证等待
生成公钥和私钥
生成公私钥的工具下载地址
按照我的箭头步骤来,官网说最好用2048的,然后点击”生成密钥“,这个时候你生成的密钥都会保存在RSA密钥文件夹下面,如果你下次要用的话,就打开文件夹里面找到应用公钥2048或是应用私钥2048。(这个工具其实你可以随便生成,想生成就怎么生成,只要你保证,你上传到支付宝的公钥和你服务器配置的公钥相同,并且私钥和公钥是匹配的就行,工具里面有个密钥匹配选项,可以匹配测试一下对不对)
公钥上传到支付宝
然后我们继续进开发者中心,找到自己新建的应用,选择“应用信息选项卡”。
这个地方我已经设置过公钥了,你们可以设置一下。然后配置基本就完成了。
代码配置
android配置
代码的地方可以看这个文档,把权限、清单文件、混淆等都配置好,然后我们开车。
首先,下载一下官网给的demo,看第三方的demo还是比较好容易理解的,我们把androidDemo打开,主要来看PayDemoActivity.java这个类
PayDemoActivity.java 我们看支付这个方法,其他的不看了,没有用
/**
* 支付宝支付业务
*
* @param v
*/
public void payV2(View v) {
/*
注释了,这部分不要,对于本地测试支付相关的都不要
if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) {
new AlertDialog.Builder(this).setTitle("警告").setMessage("需要配置APPID | RSA_PRIVATE")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
//
finish();
}
}).show();
return;
}
*/
/**
* 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;
* 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成(多么体贴的一句话)
* 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;
*
* orderInfo的获取必须来自服务端;
*/
// boolean rsa2 = (RSA2_PRIVATE.length() > 0);
//Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2);
//String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
//String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;
//String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);
//final String orderInfo = orderParam + "&" + sign;
/*
这个orderInfo我们还是需要的,支付宝要求我们这个orderInfo从后台返回给我们,不要在前台去生成,这个支付demo就是给我们演示了一下,这个orderInfo的生成,来完成支付。
所以,我们等会在这个地方去后台拿orderInfo,然后再继续接下来的流程。
android配置这个部分我们先暂停,先去后台配置一些东西。
*/
final String orderInfo="";
Runnable payRunnable = new Runnable() {
@Override
public void run() {
PayTask alipay = new PayTask(PayDemoActivity.this);
Map<String, String> result = alipay.payV2(orderInfo, true);
Log.i("msp", result.toString());
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
后端php配置
我后台使用的是php,其实其他配置都是一样的,主要看流程。
后端demo下载链接,然后我们要看看这个文档php部分的配置,java .NET都是这样的一个流程,配置好一些信息基本就ok了。
这个地方我遇到了一些坑,怪我自己没注意,列出来
(一)、php版本最少要php5.5以上的开发环境
(二)、去php的安装目录下面找到php.ini,打开该文件找到extension=php_openssl.dll,去电前面的分号,然后重启服务器,因为支付宝用的是https去验证返回的,所以,需要打开这个。
好了。来看服务端代码。
这个是官网给的demo样本,我们就跟着这个来
$aop = new AopClient;
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
$aop->appId = "app_id";
$aop->rsaPrivateKey = '请填写开发者私钥去头去尾去回车,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
$aop->alipayrsaPublicKey = '请填写支付宝公钥,一行字符串';
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
$bizcontent = "{\"body\":\"我是测试数据\","
. "\"subject\": \"App支付测试\","
. "\"out_trade_no\": \"20170125test01\","
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商户外网可以访问的异步地址");
$request->setBizContent($bizcontent);
//这里和普通的接口调用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题
echo htmlspecialchars($response);//就是orderString 可以直接给客户端请求,无需再做处理。
我们在支付宝demo中新建一个pay文件夹,然后里面新建一个orderInfo.php文件。
如图所示:
在新建的文件和文件夹中,我们将官网给出的代码粘贴进去,然后require_once引入下面要使用的类。
还是看代码舒服,看图不对劲。
<?php
//这个地方要引入下面要用到的类
require_once '../aop/AopClient.php';
require_once '../aop/request/AlipayTradeAppPayRequest.php';
$aop = new AopClient;
//这里是网关,下面这个网关是正式环境的,等会配置沙箱测试环境的时候会换一个网关
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
//填写appid,在应用的头上面有
$aop->appId = "app_id";//
//这个地方填写私钥,就是我们在上面用工具生成的私钥,这个私钥必须是和上传到支付宝的公钥匹配,不让,支付宝访问的时候会匹配错误
$aop->rsaPrivateKey = '请填写开发者私钥去头去尾去回车,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
//这个地方的公钥也是一样,必须是上传到支付宝的那个公钥要一样
$aop->alipayrsaPublicKey = '请填写支付宝公钥,一行字符串';
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
$bizcontent = "{\"body\":\"我是测试数据\","//这个地方写一些参数,在弹出支付的时候会显示
. "\"subject\": \"App支付测试\","//这个可以进行支付的一些描述
. "\"out_trade_no\": \"20170125test01\","//订单号,必须是唯一的,等会我会给一个生成订单号的函数,
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商户外网可以访问的异步地址");
$request->setBizContent($bizcontent);
//这里和普通的接口调用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题
echo htmlspecialchars($response);//就是orderString 可以直接给客户端请求,无需再做处理。
插入一个小东西,订单号生成代码
function order()
{
$yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
$orderSn = $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
return $orderSn;
}
填写完整后,我们来测试一下,访问这个链接,看看返回的是啥
app_id=2016080601711375&biz_content=%7B%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22subject%22%3A+%22App%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95%22%2C%22out_trade_no%22%3A+%2220170125test01%22%2C%22timeout_express%22%3A+%2230m%22%2C%22total_amount%22%3A+%220.01%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2Fwww.inbandian.com%2F&sign_type=RSA2×tamp=2017-04-07+20%3A13%3A33&version=1.0&sign=UYy7Kk7Jz4ZnSLa1vwasEv0MRcMMzt2dcfdXU9hWCkc0HyqjzdsU6c97ANp062XjI0YogqCXhTBLAEScnwj%2FkkNEjqeLfPn3Gaave0AExEIIruKclX1lXpMMVhotcwx%2FDLgsQvtRlyVShEOOBCib8AvXVPknNb4CEzsOg7R4Ee7RN8TBjZ46PdO786U9hbMuRDdgvn753j4yoTruUj2YGpl1SeauUeOsTFDbrHKbEFWxU%2BtrbrgeJreps0vZ2hcNBLkOag9rmx4gTWKGxV0eoQB%2FpcJjBsKLfkViWo9vs1Nxnfwso%2FxQk%2FRv37zftvRVgRPqHUXnZHveUXoBqbQm2g%3D%3D
相信用android端支付的,用android端直接进行支付的打印出的订单信息的格式肯定是和这个一样的是吧,那就对了。
好了,链接也测试通过了,那我们只需要通过android端,每次支付的时候,通过这个接口去获取订单信息,然后将这个信息通过支付宝API的
Map<String, String> result = alipay.payV2(orderInfo, true);
就可以进行支付了,下面我给出简洁后的android端代码。
public class PayDemoActivity extends FragmentActivity {
private static final int SDK_PAY_FLAG = 1;
private static final int SDK_AUTH_FLAG = 2;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_PAY_FLAG: {
@SuppressWarnings("unchecked")
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
/**
对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
*/
String resultInfo = payResult.getResult();// 同步返回需要验证的信息
String resultStatus = payResult.getResultStatus();
// 判断resultStatus 为9000则代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
Toast.makeText(PayDemoActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
} else {
// 该笔订单真实的支付结果,需要依赖服务端的异步通知。
Toast.makeText(PayDemoActivity.this, "支付失败", Toast.LENGTH_SHORT).show();
}
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_main);
}
/**
* 支付宝支付业务
*
* @param v
*/
public void payV2(View v) {
Runnable payRunnable = new Runnable() {
@Override
public void run() {
URL url = null;
try {
url = new URL("orderInfo.php的url地址");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String orderInfo = "";
String len;
while ((len = br.readLine()) != null) {
orderInfo += len;
}
Log.i("orderInfo", orderInfo);//打印一下看看对不对
PayTask alipay = new PayTask(PayDemoActivity.this);
Map<String, String> result = alipay.payV2(orderInfo, true);
Log.i("msp", result.toString());
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}catch(Exception e){
e.printStackTrace();
}
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
}
代码还是挺少的,就这样前端和后端打通了,接下来支付成功异步通知服务器什么的,就可以发挥大家自己的想法啦(反正我已经做好了,棒棒的,哈哈)。
大家在做测试的时候一想,每次都要我用0.01去测试数据,未免有点坑啊,心疼我的钱啊,刚开始不懂的时候我就充值了好几次,肉疼。
好了,下面给出沙箱测试的步骤,大致意思就是,支付宝会提供给你一个沙箱测试的账号的密码,而且还有指定的沙箱版支付宝APP,我们去下载一下这个支付宝APP,然后登陆支付宝给我们的账号,有买家和商家的账号都有,然后测试的时候,会唤起我们的这个沙箱版的支付宝,玩起来和真支付宝是一样的。
沙箱版支付宝测试
既然是拿来测试用的,免不了配置,沙箱版的配置和之前的应用配置是一模一样的。来看步骤。
配置
首先还是进开发者中心,如图,选择沙箱应用。
和之前的配置是不是又那么点像呢,哈哈,沙箱版的支付宝网关和真实环境是不一样的,我们等会去替换他,还有appid,下面的密钥配置和之前步骤一样,打开密钥工具,随机生成一个2048的密钥,将公钥张贴进去。
我们继续往下滑动,下面会有一个沙箱版的app的二维码,我们扫描一下,把他下载下来。
好了,我们现在服务端文件夹中的pay文件夹重新新建一个sandboxOrderInfo.php
<?php
//这个地方要引入下面要用到的类
require_once '../aop/AopClient.php';
require_once '../aop/request/AlipayTradeAppPayRequest.php';
$aop = new AopClient;
//这里是网关,填写沙箱版里面给的网关
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
//填写appid,填写沙箱版里面给的appid
$aop->appId = "app_id";//
//这个地方填写私钥,就是我们在上面用工具生成的私钥,这个私钥必须是和上传到支付宝的公钥匹配,不让,支付宝访问的时候会匹配错误
$aop->rsaPrivateKey = '请填写开发者私钥去头去尾去回车,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
//这个地方的公钥也是一样,必须是上传到支付宝的那个公钥要一样
$aop->alipayrsaPublicKey = '请填写支付宝公钥,一行字符串';
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
$bizcontent = "{\"body\":\"我是测试数据\","//这个地方写一些参数,在弹出支付的时候会显示
. "\"subject\": \"App支付测试\","//这个可以进行支付的一些描述
. "\"out_trade_no\": \"20170125test01\","//订单号,必须是唯一的,等会我会给一个生成订单号的函数,
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商户外网可以访问的异步地址");//这个异步地址是自己在开发者中心我的应用中配置的
$request->setBizContent($bizcontent);
//这里和普通的接口调用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题
echo htmlspecialchars($response);//就是orderString 可以直接给客户端请求,无需再做处理。
此处和之前只是环境的一样,只需要修改app_id,网关,和对应的公钥和私钥就行了。好了,服务端的又配置好了。
我们来打开开发者中心的“沙箱账号”。如图
沙箱会给我们一个商家的和买家的账号,便于我们测试用,看到充值那个选项没,真的好像是我的真实账号啊,想要多少就自己填,好爽。然后我们用这个账号登陆我们的沙箱版APP,登陆成功我们就不需要去管他了,就当他是我们真是的支付宝账号。
然后回到我们的android客户端,我们在onCreate初始化的时候加上这么一句代码,告诉支付宝,我们这是处于沙箱环境。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_main);
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
}
然后我们将访问获取订单信息的url改成sandboxOrderInfo.php,然后进行访问获取,去支付,OK,大功告成。想怎么支付就怎么支付,好爽。
前端到后端结合还是挺简单的,跟着这个思想套路走,java和ASP都是一样的。
我要下楼去吃全套酸奶去了,味道啊!