昨天整理了关于APP微信支付的东西,今天在整理代码之后,来谈谈支付宝的APP支付。
两者有很大相似之处,也有区别,只要理解了一个,另一个就很好理解了,如果是第一次做服务端的支付的话,建议先看看上面那篇APP微信支付,我是从微信支付那边过来的,再看支付宝支付,就感觉很顺利了。
闲话少说,开始发招!
先看一下开发文档,创建应用:https://docs.open.alipay.com/200/105310/
创建自己的应用之后,拿到一些开发中使用的信息,具体请看下面的配置文件里的信息。
首先是基础的配置文件AlipayConfig.java
package com.thinkgem.jeesite.modules.alipay;
public class AlipayConfig {
// 合作身份者ID,以2088开头由16位纯数字组成的字符串
public static String partner = "2088**********3";
// 6.请求网关地址
public static String URL = "https://openapi.alipay.com/gateway.do";
public static String service = "mobile.securitypay.pay";//固定值
//商家账号
public static String seller_id = "[email protected]";
//私钥
public static String private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCUED+fmRDKV47vmJFtMl8EVZYlLHRCc98WP6QG19UP5J0EZsoSi/rOkG/LEHn83i5+uIfoMEvnr5/K9vvviulzdnrne3w852H7UgcSX/WyBQeamGH+8K0ReXVs2G+jno9oBoi5x5wchIjhoZ9eRRfk0Q89HVtcGjnkwqsXLHOAq3Ckpw5gfuzrXAV8RFS38hoBVs9e+fP+yZN6CEsJc0gHNsyBmm4/lhBhzKK1GFmHQ2iAJUI+pLjuYekILSioPChUFdnGkveRuk1xFN+dFW322P9sJI5E2cvkjZn1B47K3xIBD/UO14tmRL72JKGS6/QAltxa6AUBTAgMBAAECggEAbU1Bax51W5cJXYOdfsfcg/M+mStbyFnVx5FntOpvTXsIJhSJ4q09pi9st9XWwHRIhiuFeqU+GWUZXF6R4laZxonym8kGd/8F7OcT5YWNW/hmivnOaf5LwxIFlVQpyAlAfaHmQcr9R8gqmi9lNRXUn4fcxUsf5+2IGxuMwpZRg6Vz8P1pIlMZkK3XDtYSxHue9LluttzvZRIFuX23dNa2xWP392ClregMQfauv7zFh+UtmgC1xA7L+d6borg7eQSV6ic3j0rw5UOA8fAqxd2Qi4TMzDtASW5Y2tQemN3N+S3niqM2K/dKSN1wQKBgQDJKVMpuWSUgPs3o81sfdasfaIh2BaHmTTgx7bQyOyEK4dAWLA7L6pCKba1jgMkP15jP/WOupNN5w/LqrQxC1mta8rcqg+WjkzfYw4gCfmwKlafmfCu5IYALmTu38GMJMPdT4Gc7alvqU6+qF1VVpVZ5DBpuGL7XYQ6FVXTMWwKBgQC8bVFj3UDXwioB7GJ04kAhGSTGerNpIgNQ48P6+88jhQmtrURBq33AonRce7c8Zwm/acdaQoJlbrfN6cUMDJg7ZYWoQfdWfnBcS8oRosFyMP6LCPNuyOXEZrTxs5QkdQ7OfeghjKzBkKZksA/oahzqiFwOMftMN9aQKBgQC3lTFCaOFz7epWsvTsd0f1WhkQ4rLjBBtl29vJS6c5vJxjWXJXas1PLU3UwjA2G+wHMpJlyto70CH9dNxkWgdz3VYmoF3pu3DV5y265mCTHBd23uBaaHZYkcz2knK1Zug08YzdzwWutiADM49DTHV49bWQ1vfhJvI69EQKBgF5aW0QneZ3Qw1o9NTwUQ0qPnaOHlfp8tskiluyFWgrM+LQy5Q/ofHydkZ+feLLplVWzN33beI6iyeJA9oA4MAC08HrfQVh3P/Kj7/vAPEeaYfTjppuDOghCgByDP+IgrRm5+Tq8FiqtcuiLOomf6mjB5A89/D1XNgDpAoGAREPrq9QW2kiZfujfseHX98my8Bop0O+GEvnbBTVa7t34QlfuKorfRBoCy0zPeqDxzLzODhDD3+7iAtL3o/kk7/HoYRLeEaEoCv978c9GQxL+8fYuyNYRt2IOTxqVv2+hOS6Gll8vlyZ6iPgsx2kpcDhINpQUBMGGIBbOLhWFdEoD7Y=";
// 商户的公钥钥
public static String public_key ="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEsAlBA/n5kQyleO75iRbfsTJfBFWWJSx0QnPfFj+kBtfVD+SdBGbKEov6zpBvyxB5/N4ufriH6DBL56+fyvb774rpsc3Z653t8POddh+1IHEl/1sgUHmsdphh/fdvCtEXl1bNhvo56PaAaIucecHISI4aGfXkUX5NEPPawWUR1bXBo55MKrFyxFPQxRLQchzgKtwpKcOYhH7s61wFfERUt/IaAVbPXvnz/smTeghLCXNIBzbMgZpuP5YQYcyitRhZh0NogCVCPqS47mHpCC0oqDwoVBXZxpL3kbpNcRTfnRVt9tj/bCSORNnL5I2fdZ9QeOyt8SAQ/1DteLZkS+9iShkuv0AJbcWugFAUwIDAQAB";
// 支付宝的公钥,无需修改该值(不要删除也不要修改,在接收通知的时候需要进行签名认证)
public static String ali_public_key= "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQWVkXQUeUrfL6Dxya22OY+qlS2Q13MwHOk4c2joFERAuOzh5E6ckyHGI1jXXIwHjZDkjH8XR0q4jJjj32AX9Oi/phYObO65+WKrrVpxYzfLqQSttpjIsbI48AL4rswqW0CeS+MTYhZv6hwuLnB5q1FClV7pg8lWBtp25L2ikasnZ19D8b8a/+yBphbBGjNn4RiArZwthpZJtpM7LgqyXMrZYtk5gi5hBLBzPs16Q71erDRRsHK9OLxahgNyX6C5q9kHflKvYcOY61QPqghsU3Gq+Z2wkp2oOJ4QssQ9c8+vbO/tcDOLYosacOJhUy0xFmS2nDX7eCSnoUFZMnL7tQIDAQAB";
// 字符编码格式 目前支持 gbk 或 utf-8
public static String input_charset = "utf-8";
// 签名方式 不需修改
public static String sign_type = "RSA2";
//APPID
public static String APPID = "2018060507535980";
// 支付宝支付回调
public static String NOTIFY_URL = "http://花生壳ip/ejtapp/PayCtrl/AliPayNotify.do";
// 8.返回格式
public static String FORMAT = "json" ;
}
有了这些数据之后,我们便可以开始写支付的接口了。
首先是APP端访问此接口,参数为订单id,服务端将订单数据传到支付宝,换取支付参数返回给APP端
/**
* 支付宝会员充值
*
* @param request
* @param response
* @param data
* @return
*/
@RequestMapping("/AliPayRecharge")
@ResponseBody
public JSONObject AliPayRecharge(HttpServletRequest request, HttpServletResponse response, String data) {
System.out.println("=======续费充值支付======");
JSONObject obj = JSON.parseObject(data);
String orderId = obj.getString("orderId");
ShopMemberRechargeRecord findByOrderId = shopMemberRechargeRecordService.findByOrderId(orderId);
String aliPay = alipayUtil.aliPay(findByOrderId);
System.out.println("=======返回信息======");
return JsonUtil.getJson(10, aliPay, null);
}
如果有多种订单,可以共用一个支付接口,这时多了一个订单类型的参数,此参数决定了后面要传入的订单实体类,如下
/**
* 支付宝支付接口获取支付参数给前端app
*
* @param request
* @param response
* @param data
* @return
*/
@RequestMapping("/AliPay")
@ResponseBody
public JSONObject AliPay(HttpServletRequest request, HttpServletResponse response, String data) {
System.out.println("===============支付宝统一下单接口==============");
JSONObject obj = JSON.parseObject(data);
String orderId = obj.getString("orderId");
String orderType = obj.getString("orderType"); // 如果有多种订单需要支付,便可以加上此参数
System.out.println("============orderType:" + orderType + "==============");
String aliPay = null;
if (orderType.equals("order")) {
// 商品订单
ShopOrder findByOrderId = shopOrderService.findByOrderId(orderId);
aliPay = alipayUtil.aliPay(findByOrderId);
}else if (orderType.equals("renew")) {
// 会员续费订单
ShopMemberRechargeRecord findByOrderId = shopMemberRechargeRecordService.findByOrderId(orderId);
aliPay = alipayUtil.aliPay(findByOrderId);
}else {
// 余额充值订单
ShopPdRecharge recharge = shopPdRechargeService.selectByPdrSn(orderId);
try {
aliPay = alipayUtil.aliPay(recharge);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
System.out.println("===============支付宝统一下单失败==============");
return JsonUtil.getJson(20, "支付宝统一下单失败", null);
}
}
System.out.println("============接口返回参数aliPay:" + aliPay + "==============");
System.out.println("===============支付宝统一下单结束==============");
return JsonUtil.getJson(10, aliPay, null);
}
然后是alipayUtil.java
package com.thinkgem.jeesite.modules.alipay;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.thinkgem.jeesite.modules.ejt.entity.ShopMemberRechargeRecord;
import com.thinkgem.jeesite.modules.ejt.entity.ShopOrder;
import com.thinkgem.jeesite.modules.ejt.entity.ShopPdRecharge;
public class alipayUtil {
//实例化客户端
public static AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.private_key , AlipayConfig.FORMAT, AlipayConfig.input_charset, AlipayConfig.public_key, AlipayConfig.sign_type);
/**
* 商品订单
* @param order
* @return
* @throws UnsupportedEncodingException
*/
public static String aliPay(ShopOrder order) throws UnsupportedEncodingException{
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//model.setPassbackParams(URLEncoder.encode(body.toString()));; //描述信息 添加附加数据
model.setSubject("e境通-商城消费"); //商品标题
model.setOutTradeNo(order.getOrderSn()+""); //商家订单编号
model.setTimeoutExpress("30m"); //超时关闭该订单时间
model.setTotalAmount(order.getOrderAmount()); //订单总金额
model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
model.setPassbackParams(URLEncoder.encode("order", "GBK")); //公用回传参数passback_params
request.setBizModel(model);
request.setNotifyUrl(AlipayConfig.NOTIFY_URL); //回调地址
String orderStr = "";
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
orderStr = response.getBody();
System.out.println(orderStr);//就是orderString 可以直接给客户端请求,无需再做处理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
return orderStr;
}
/**
* 余额充值
* @param recharge
* @return
* @throws UnsupportedEncodingException
*/
public static String aliPay(ShopPdRecharge recharge) throws UnsupportedEncodingException{
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//model.setPassbackParams(URLEncoder.encode(body.toString()));; //描述信息 添加附加数据
model.setSubject("e境通-余额充值"); //商品标题
model.setOutTradeNo(Long.toString(recharge.getPdrSn())); //商家订单编号
model.setTimeoutExpress("30m"); //超时关闭该订单时间
model.setTotalAmount(recharge.getPdrAmount()); //订单总金额
model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
model.setPassbackParams(URLEncoder.encode("recharge", "GBK")); //公用回传参数passback_params
request.setBizModel(model);
request.setNotifyUrl(AlipayConfig.NOTIFY_URL); //回调地址
String orderStr = null;
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
orderStr = response.getBody();
//System.out.println(orderStr);//就是orderString 可以直接给客户端请求,无需再做处理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
return orderStr;
}
/**
* 会员充值订单
* @param findByOrderId
* @return
* @throws UnsupportedEncodingException
*/
public static String aliPay(ShopMemberRechargeRecord findByOrderId) throws UnsupportedEncodingException {
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//model.setPassbackParams(URLEncoder.encode(body.toString()));; //描述信息 添加附加数据
model.setSubject("e境通-会员充值"); //商品标题
model.setOutTradeNo(findByOrderId.getMrrSn()+""); //商家订单编号
model.setTimeoutExpress("30m"); //超时关闭该订单时间
model.setTotalAmount(findByOrderId.getMrrAmount()); //订单总金额
model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
model.setPassbackParams(URLEncoder.encode("renew", "GBK")); //公用回传参数passback_params
request.setBizModel(model);
request.setNotifyUrl(AlipayConfig.NOTIFY_URL); //回调地址
String orderStr = "";
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
orderStr = response.getBody();
System.out.println(orderStr);//就是orderString 可以直接给客户端请求,无需再做处理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
return orderStr;
}
}
这里alipayUtil 中的aliPay方法内部,使用的参数可以在文档中找到:
可以将上面文档中的业务参数放到model里面,具体参考model.setPassbackParams(URLEncoder.encode("recharge", "GBK")); //公用回传参数passback_params
。
APP端调用AliPay
接口之后,拿到支付参数,即可调其支付宝支付页面。
然后再用户输入支付密码之后,支付宝开始访问AlipayConfig.NOTIFY_URL
这个回调地址,这里需要保证回调地址可以用外网访问得到,如果没有域名,可以使用花生壳,做一个内网渗透即可,花生壳的使用,请参考这篇APP微信支付。
那么回调方法是怎样的呢?即是支付宝文档中的异步通知接口:https://docs.open.alipay.com/204/105301/
具体的参数请参考文档,下面的触发条件说明,当用户支付成功之后,支付宝会触发异步回调通知。
接着看回调方法:
/**
* 支付宝回调
*
* @param request
* @param response
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping("/AliPayNotify")
@ResponseBody
public String AliPayNotify(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
System.out.println("==========支付宝统一回调==========");
Map<String, String> params = new HashMap<String, String>();
// 1.从支付宝回调的request域中取值
@SuppressWarnings("unchecked")
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
}
System.out.println("params:" + params);
String orderType = params.get("passback_params"); //公用回传参数订单类型
System.out.println("公用回传参数orderType:" + orderType);
String outTradeNo = params.get("out_trade_no");
String paySn = params.get("trade_no");
String totalFee = params.get("total_amount");
boolean signVerified = false;
// 调用SDK验证签名
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ali_public_key, AlipayConfig.input_charset,
AlipayConfig.sign_type);
} catch (AlipayApiException e) {
e.printStackTrace();
}
System.out.println("=======验证签名:" + signVerified + "======");
if (signVerified) {
System.out.println("=================================================");
System.out.println("=============支付成功,下面开始更新订单数据===========");
System.out.println("=================================================");
boolean notifyUpdate = notifyUpdate(orderType, outTradeNo, paySn, "alipay", totalFee);
if (notifyUpdate) {
return "success";
}
}
return "failure";
}
注意回调方法,返回的数据是有要求的,如果更新订单成功需要返回"success"
,否则返回"failure"
。
如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8
次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h
)
最后是查询订单接口,即用服务器主动查询订单支付状态,然后返回给APP端:
/**
* 支付宝查询订单接口
*
* @param request
* @param response
* @param data
* orderType 订单类型
* result 支付结果 支付结果是APP输入交易密码之后 支付宝返回的信息
* @return
*/
@ResponseBody
@RequestMapping("AliPayRecordQuery")
public JSONObject AliPayRecordQuery(HttpServletRequest request, HttpServletResponse response, String data) {
System.out.println("===============支付宝订单查询接口==============");
try {
JSONObject json = JSON.parseObject(data);
String orderType = json.getString("orderType");
System.out.println("=======订单类型:" + orderType + "======");
String result = json.getString("result");
System.out.println("result:" + result);
JSONObject jsonResult = JSON.parseObject(result);
String AliPayResponse = jsonResult.getString("alipay_trade_app_pay_response");
System.out.println("AliPayResponse:" + AliPayResponse);
JSONObject jsonResp = JSON.parseObject(AliPayResponse);
String outTradeNo = jsonResp.getString("out_trade_no");
String paySn = jsonResp.getString("trade_no");
String totalFee = jsonResp.getString("total_amount");
Map<String, String> params = JSONObject.parseObject(jsonResp.toJSONString(),
new TypeReference<Map<String, String>>() {
});
String sign = jsonResult.getString("sign");
params.put("sign", sign);
boolean re = AlipaySignature.rsa256CheckContent(AliPayResponse, sign, AlipayConfig.ali_public_key,
AlipayConfig.input_charset);
// AlipaySignature.rsa256Sign(content, privateKey, charset)
if (jsonResp.getString("code").equals("10000")) {
System.out.println("==================此订单已支付成功================");
System.out.println("==============下面做判断订单状态是否更新=============");
System.out.println("========如果没有更新需要更新,证明回调里面出现异常=============");
boolean queryAndUpdate = queryAndUpdate(orderType, "alipay", outTradeNo, paySn, totalFee);
if (queryAndUpdate) {
System.out.println("===============支付宝订单查询成功==============");
return JsonUtil.getJson(10, null, "支付成功");
}
return JsonUtil.getJson(30, null, "支付成功,系统内部错误"); // 回调失败情况下 订单查询中修改订单状态出错
} else {
return JsonUtil.getJson(20, null, "支付失败");
}
} catch (Exception e) {
return JsonUtil.getJson(40, null, "系统内部错误");
}
}
这里将我用到的订单支付成功之后更新数据库的方法,也贴出来,刻意忽略下面这些方法。
/**
* 回调函数中更新数据库订单或记录
* @param orderType 订单种类:order订单,renew续费或注册, recharge充值
* @param orderNum 订单编号
* @param paySn 支付单号
* @param payType 支付方式 wechat alipay
* @param totalFee 实际支付金额
* @return
*/
public boolean notifyUpdate(String orderType, String orderNum, String paySn, String payType, String totalFee){
System.out.println("totalFee:" + totalFee);
if (orderType.equals("order")) {
ShopOrder findByOrderNo = shopOrderService.findByOrderNo(orderNum);
System.out.println("orderFee:" + findByOrderNo.getOrderAmount());
if (totalFee.equals(findByOrderNo.getOrderAmount())) {
findByOrderNo.setPaySn(paySn);
findByOrderNo.setOrderState("20");
findByOrderNo.setPaymentCode(payType);
findByOrderNo.setPaymentTime(ZoscDateUtil.getTime());
// 更新订单状态
int selective = shopOrderService.updateByPrimaryKeySelective(findByOrderNo);
if (selective > 0) {
System.out.println("===============更新商品订单成功==============");
return true;
}
}
}else if (orderType.equals("renew")) {
boolean updateRecordLog = updateRecordLog(orderNum, payType, paySn, totalFee);
return updateRecordLog;
}else {
ShopPdRecharge recharge = shopPdRechargeService.selectByPdrSn(orderNum);
if (totalFee.equals(recharge.getPdrAmount())) {
recharge.setPdrPaymentCode(payType);
if (payType.equals("wechat")) {
recharge.setPdrPaymentName("微信");
}else {
recharge.setPdrPaymentName("支付宝");
}
recharge.setPdrTradeSn(paySn);
recharge.setPdrPaymentTime(ZoscDateUtil.getTime());
recharge.setPdrPaymentState("1");
int updateRecharge = shopPdRechargeService.updateByPrimaryKeySelective(recharge);
ShopMember member = shopMemberService.findByMemberId(recharge.getPdrMemberId());
String oldBalance = member.getAvailablePredeposit();
String newBalance = BigDecimalUtil.strAdd(oldBalance, recharge.getPdrAmount());
member.setAvailablePredeposit(newBalance);
int updateMember = shopMemberService.updateByPrimaryKeySelective(member);
if (updateRecharge + updateMember > 1) {
System.out.println("===============更新充值订单成功==============");
return true;
}
}
}
return false;
}
/**
* 订单查询 判断订单状态是否更新完成模块
* @param orderType
* @param payType
* @param orderNum
* @param transactionId
* @return boolean
*/
public boolean queryAndUpdate(String orderType, String payType, String orderNum, String transactionId, String totalFee){
boolean notifyUpdate = false;
System.out.println("orderType:" + orderType);
if (orderType.equals("order")) {
ShopOrder findByOrderNo = shopOrderService.findByOrderNo(orderNum);
System.out.println("OrderState:" + findByOrderNo.getOrderState());
if (findByOrderNo.getOrderState().equals("20")) {
notifyUpdate = true;
} else {
notifyUpdate = notifyUpdate(orderType, orderNum, transactionId, payType, totalFee);
}
}else if (orderType.equals("renew")) {
ShopMemberRechargeRecord rechargeRecord = shopMemberRechargeRecordService.findByOrderId(orderNum);
if (rechargeRecord.getMrrPaymentState().equals("1")) {
notifyUpdate = true;
}else {
notifyUpdate = notifyUpdate(orderType, orderNum, transactionId, payType, totalFee);
}
}else {
ShopPdRecharge recharge = shopPdRechargeService.selectByPdrSn(orderNum);
if (recharge.getPdrPaymentState().equals("1")) {
notifyUpdate = true;
}else {
notifyUpdate = notifyUpdate(orderType, orderNum, transactionId, payType, totalFee);
}
}
return notifyUpdate;
}
/**
* 会员续费支付成功之后更新数据库模块
* @param orderNum 订单编号
* @param payCode 支付方式
* @param serialNum 支付单号
* @param totalFee 实际支付金额
* @return
*/
public boolean updateRecordLog(String orderNum, String payCode, String serialNum, String totalFee){
ShopMemberRechargeRecord rechargeRecord = shopMemberRechargeRecordService.findByOrderId(orderNum);
if (totalFee.equals(rechargeRecord.getMrrAmount())) {
ShopMemberRechargeLog rechargeLog = shopMemberRechargeLogService.selectByMrrSn(rechargeRecord.getMrrSn());
// 更新record表
String mrrAddTime = ZoscDateUtil.getTime();
rechargeRecord.setMrrAddTime(mrrAddTime);
rechargeRecord.setMrrPaymentState("1");
if (payCode.equals("alipay")) {
rechargeRecord.setMrrPaymentName("支付宝");
rechargeRecord.setMrrPaymentCode("alipay");
}else{
rechargeRecord.setMrrPaymentName("微信");
rechargeRecord.setMrrPaymentCode("wechat");
}
rechargeRecord.setMrrTradeSn(serialNum);
// 判断支付类型
// 获取当前会员的有效期限
ShopMemberRechargeRecord lastRenewRecord = shopMemberRechargeRecordService.selectLastRenew(rechargeRecord.getMrrMemberId());
String mrrExpirationTime = lastRenewRecord.getMrrExpirationTime();
System.out.println("到期时间是否为0:" + mrrExpirationTime);
long expirationTime = Long.parseLong(mrrExpirationTime);
long now = System.currentTimeMillis()/1000;
Long yearLater = null;
if ((expirationTime == 0) || (now > expirationTime)) {
// 第一次注册会员 或者 会员已过期续费
Date date = new Date();
yearLater = DateUtil.getDaysLater(date, 365);
System.out.println("首次注册会员有效期:" + yearLater);
}else {
// 会员有效期内续费
yearLater = DateUtil.timestampDaysLater(mrrExpirationTime, 365);
}
rechargeRecord.setMrrExpirationTime(Long.toString(yearLater));
System.out.println(rechargeRecord);
shopMemberRechargeRecordService.updateByPrimaryKeySelective(rechargeRecord);
// 更新log表
rechargeLog.setMrlAddTime(mrrAddTime);
rechargeLog.setExpirationTime(rechargeRecord.getMrrExpirationTime());
String rechargeType = rechargeLog.getRechargeType();
String rechargeState = null;
String rechargeDesc = null;
if ("recharge".equals(rechargeType)) {
rechargeState = "充值成功";
rechargeDesc = "充值成功,充值单号:" + orderNum;
} else {
rechargeState = "续费成功";
rechargeDesc = "续费成功,充值单号:" + orderNum;
}
rechargeLog.setRechargeState(rechargeState);
rechargeLog.setRechargeDesc(rechargeDesc);
rechargeLog.setMrrAmount(rechargeRecord.getMrrAmount());
shopMemberRechargeLogService.updateByPrimaryKey(rechargeLog);
// 更新会员标志
ShopMember member = shopMemberService.findByMemberId(rechargeRecord.getMrrMemberId());
if ("0".equals(member.getIsPlatformMember())) {
member.setIsPlatformMember("1");
shopMemberService.updateByPrimaryKeySelective(member);
}
System.out.println("=======会员充值更新数据库完毕=======");
return true;
}
return false;
}
到这里APP支付宝支付就算结束了,有什么问题或建议,欢迎评论交流。
这次换了一个项目,又写了一遍支付,又发现了一些新的问题。
第一点就是,刚开始由于产品那边给的支付宝账号下面申请了好几个应用,我直接去项目名那个应用下面,一顿复制粘贴,弄好了,就兴高采烈的让安卓测试,然后问题就来了。。。。。
第一次测的时候,安卓那边点击付款,直接报错了,提示【交易订单处理失败 请稍后再试 ALI38173】
,然后安卓问我,是啥情况,他那边也是从别的项目里直接拷贝过来的,问我能不能用,我也是一脸懵逼,我也不知道安卓怎么处理返回的预支付订单信息,然后我就搜了一下,这一搜不要紧,搜到蚂蚁金服社区文档里,有这么一说:
然后我就回去对比我写的参数,发现,之前项目里传的参数,这里我也都传了,怎么还会报错呢????
然后又开始对比开发文档里面的请求参数,发现有几个确实没传,但是之前的项目也没有传,就能正常使用。可能不是因为这个。然后安卓那边给我说好像写错了,改了一下再试试。
然后我就等,那边说改好了测了一下,又出现新的问题了。
然后我突然想到,现在使用的参数是同事给我的,会不会出错了,然后我就要来账号,自己去蚂蚁金服开发平台去找,然后找到了项目名对应的应用,发现这里虽然显示了已上线,但是好像没有添加APP支付的功能。
这肯定行不通。。。。。。。然后找组长问问,这个要不要用另一个项目里的数据进行测试,然后组长给我说,就是用另一个。。。。。
对比一下这个,里面是有签约的APP支付的功能的。
刚开始没有想到换新的私钥和公钥,后来同事给我说,要不你重新生成一对秘钥,然后修改一下支付宝上的。
我这才重新生成秘钥,生成秘钥的工具,蚂蚁金服开发平台上也有,这里提供一个传送门。很详细的生成一对新的秘钥,然后将公钥设置在应用的后面,如下图:
换了之后,一次就成功了,真的舒服。。。。。。。
但是到查询订单的时候,按照上面的写法,安卓那边需要将支付的结果result
传过来,之前那个项目,是安卓直接传的result了,我直接在后面截取,然后判断("code").equals("10000")
这个条件了,但是感觉怪怪的,其中有一句验签那个AlipaySignature.rsa256CheckContent()
也没有使用,当时也没有想着查一下,结果这次,安卓那边不知道怎么取result
这个值了,这就让我很为难了。
再后来安卓找到result参数之后,一直查询支付失败,我看了一下后发现,我又将AlipaySignature.rsa256CheckContent()
的结果和jsonResp.getString("code").equals("10000")
这个条件取并集,来验证,结果一直是支付失败,哈哈。。。这里着实把安卓给坑了一把。。。。。。。
然后我打印这个AlipaySignature.rsa256CheckContent()
一直是false,查了很多,都说第一步要验证是不是使用了支付宝的公钥,我查了一下,这个方法里面确实是用了支付的公钥,后来发现预下单支付接口里面,我使用的却是自己生成的公钥和私钥,进行加密的,导致这里一直失败。
然后我查到别人写的关于查询订单支付状态的接口,感觉就比我的好,这里记录一下:
这里不用再让安卓传result
了,直接传个订单编号或者id就好了。这里我将查询方法抽离出来了。
/**
* 查询订单使用
* @param orderId
*/
public static boolean checkAlipay(long orderId){
logger.info("==================向支付宝发起查询,查询商户订单号为:"+orderId);
//实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
alipayTradeQueryRequest.setBizContent("{" +
"\"out_trade_no\":\""+orderId+"\"" +
"}");
try {
AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(alipayTradeQueryRequest);
System.out.println("alipayTradeQueryResponse:" + alipayTradeQueryResponse);
return alipayTradeQueryResponse.isSuccess(); // 不知道为什么能这样返回,但是能用
} catch (AlipayApiException e) {
e.printStackTrace();
return false;
}
}
返回的这一句,也可以换一下,方法类型直接返回AlipayTradeQueryResponse
,然后在后台请求的查询接口里面直接获取需要的参数就好了,后面需要的流水单号,订单编号,支付金额,都可以在AlipayTradeQueryResponse
这个实体类里面直接使用get方法取到。比如获取订单金额,直接使用alipayTradeQueryResponse.getTotalAmount()
这样的话,查询订单的接口里面就很简单了,只需要判断数据库是否更新就好了。
这次踩坑得益于这篇博客,没看懂的同学,可以看一下这篇博客,很详细:https://blog.csdn.net/ouyzc/article/details/79551714
文章浏览阅读688次。前言ArrayList 底层是基于数组实现的,不过得益于其扩容机制,它可以看作是一个动态的数组,弥补了数组长度是定长的缺陷。在往 ArrayList 里添加元素时,如果添加完元素之后,元素的总个数大于当前数组的容量,会执行扩容,这个扩容的过程可以归纳为以下两个步骤:确定新数组的容量将老数组内的元素拷贝到新数组中去确定新数组的容量这一步是本文重点,接下来我们一起来看一下。 ..._arraylist阈值
文章浏览阅读1.1k次。web.assets_backend.js:3381 Could not get content for /web_drop_target/static/lib/base64js.min.js defined in bundle 'web.assets_backend'.(匿名) @ web.assets_backend.js:3381vis-timeline-graph2d.css:1 Failed to load resource: the server responded with a statu_depnode.fetchingfiles is not a function
文章浏览阅读6.2k次,点赞5次,收藏15次。**1.什么是Base64?**Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来标识二进制数据的方法。Base64是一种可逆的编码方式,是一种用64个Ascii字符来表示任意二进制数据的方法。主要用于将不可打印字符转换为可打印字符,或者简单的说将二进制数据编码为Ascii字符,**2.什么时候使用Base64?**1.B..._base64
文章浏览阅读5.7k次。非微信公众号web网页二次分享链接中图片丢失的问题_非公众号网页 分享链接
文章浏览阅读4.3k次,点赞3次,收藏7次。MPEG DASH也是一个主流的直播点播流媒体协议,而且兼容HLS。只不过相对复杂,下面作为简介进行介绍。首先,解析一个dash流需要对应MPD文件(Media Presentation Description),相当于hls的m3u8文件。MPD文件是xml格式的。下面从外到内介绍MPD文件的主要成员。1. period:一个mpeg dash流由1~n个period组成_mpd中type = static代表什么
文章浏览阅读362次。读书不觉已春深,一寸光阴一寸金。java的设计模式大体上分为三大类: 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问..._java 策略模式,享元模式
文章浏览阅读418次。4.3 路由排除配置# ignored service id pattern# 配置不被zuul管理的服务列表。多个服务名称使用逗号’,'分隔。# 配置的服务将不被zuul代理。zuul.ignored-services=eureka-application-service# 此方式相当于给所有新发现的服务默认排除zuul网关访问方式,只有配置了路由网关的服务才可以通过zuul网关访问# 通配方式配置排除列表。zu_zuul项目 禁用hystrix stream
文章浏览阅读96次。链接:https://juejin.im/post/6854573215726075917“由于有一条业务线不理想,高层决定下架业务。对于我们技术团队而言,其对应的所有服务器资源和其他相关..._熬了一个通宵,终于把7千万个key删完了
文章浏览阅读1.6k次,点赞3次,收藏2次。第一次安装scrapy常见错误及解决手段(1)运行命pip install scrapy 在执行到“Collecting Twisted>=13.1.0 (from Scrapy)”时报错:Exception:Traceback (most recent call last).....原因:我当前的版本是pip 10.01,执行下面命令会更新成最新版本。运行命令:..._scrapy mitmproxy 7.0.4 requires h11<0.13,>=0.11, but you have h11 0.14.0 whi
文章浏览阅读1.4k次。开始想用出口来做,但试了几个都不行,于是还是找了这个BADI在其中的PROCESS_ITEM方法中编写代码即可实现本列中,要求 bednr 不能为空,代码如下: DATA : wa_item TYPE mereq_item. CALL METHOD im_item->get_data RECEIVING re_data = wa_item. " BEDNR不能为空_me_process_req_cust
文章浏览阅读6k次。go pprof简介 profile 一般被称为 性能分析,词典上的翻译是 概况(名词)或者 描述…的概况(动词)。对于计算机程序来说,它的 profile,就是一个程序在运行时的各种概况信息,包括 cpu 占用情况,内存情况,线程情况,线程阻塞情况等等。知道了程序的这些信息,也就能容易的定位程序中的问题和故障原因 。pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutin_go pprof
文章浏览阅读1w次。今天在打开虚拟机的时候,遇到过一个故障:就是用虚拟机打开Ubuntu系统半天没反应,刚开始以为是电脑反应太慢了,之后,在关闭虚拟机,重新打开,等上半天还是没反应。之后在网上查找了一下,听说是VMware tools的原因。VMware tools有個功能,就是從縮主機複製文件到虛擬系統,或者從虛擬系統複製文件到縮主系統。很方便,但有時會出現故障。我只能说可能是这个原因!因为之前重新安装过V_vmware workstation16快捷方式打不开