昨天整理了关于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
文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大
文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码
文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版
文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗
文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程
文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0
文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader
文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型
文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写
文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录
文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点
文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文