做了许多微信、支付宝支付,却没写记录,尴尬,今天记录下。
要点:
- 直接去支付宝下支付接入demo,引入到IDE中
- 选择自己业务需要的功能接入
- 蚂蚁开放平台的回调地址要与支付调用时传入的地址一致
直接撸代码
目录结构:
以下是我的目录及所用到的模块
Maven:
<!-- 阿里支付sdk alipay-sdk-java -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.0.0</version>
</dependency>
ALiOrderInfoUtil2:
package com.hakj.express.utils.alipayutils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
/**
*
*/
public class ALiOrderInfoUtil2 {
/**
* 构造授权参数列表
*
* @return
*/
public static Map<String, String> buildAuthInfoMap(boolean rsa2) {
Map<String, String> keyValues = new HashMap<String, String>();
// 商户签约拿到的app_id,如:2013081700024223
keyValues.put("app_id", ALiPayConfig.APPID);
// 商户签约拿到的pid,如:2088102123816631
keyValues.put("pid", ALiPayConfig.PID);
// 服务接口名称, 固定值
keyValues.put("apiname", "com.alipay.account.auth");
// 商户类型标识, 固定值
keyValues.put("app_name", "mc");
// 业务类型, 固定值
keyValues.put("biz_type", "openservice");
// 产品码, 固定值
keyValues.put("product_id", "APP_FAST_LOGIN");
// 授权范围, 固定值
keyValues.put("scope", "kuaijie");
// 商户唯一标识,如:kkkkk091125
// keyValues.put("target_id", target_id);
// 授权类型, 固定值
keyValues.put("auth_type", "AUTHACCOUNT");
// 签名类型
keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
return keyValues;
}
/**
* 构造支付订单参数列表
*
* @return 返回未签名的共有参数
*/
public static Map<String, String> buildOrderParamMap(double price, String openid, String orderName, String orderType) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Map<String, String> keyValues = new HashMap<String, String>();
keyValues.put("app_id", ALiPayConfig.APPID);
keyValues.put("biz_content",
"{\"timeout_express\":\"30m\"," +
"\"product_code\":\"QUICK_MSECURITY_PAY\"," +
"\"total_amount\":\"" + price + "\"," +
"\"subject\":\""+orderName+"\"," +
"\"body\":\""+orderType+"\"," +
"\"out_trade_no\":\"" + openid + "\"}");
keyValues.put("charset", ALiPayConfig.input_charset);
keyValues.put("method", "alipay.trade.app.pay");
keyValues.put("sign_type", ALiPayConfig.sign_type);
String format = simpleDateFormat.format(new Date());
keyValues.put("timestamp", format);
keyValues.put("notify_url", ALiPayConfig.notify_url);
keyValues.put("version", "1.0");
return keyValues;
}
/**
* 构造支付订单参数信息
*
* @param map 支付订单参数
* @return
*/
public static String buildOrderParam(Map<String, String> map) {
List<String> keys = new ArrayList<String>(map.keySet());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.size() - 1; i++) {
String key = keys.get(i);
String value = map.get(key);
sb.append(buildKeyValue(key, value, true));
sb.append("&");
}
String tailKey = keys.get(keys.size() - 1);
String tailValue = map.get(tailKey);
sb.append(buildKeyValue(tailKey, tailValue, true));
return sb.toString();
}
/**
* 拼接键值对
*
* @param key
* @param value
* @param isEncode
* @return
*/
private static String buildKeyValue(String key, String value, boolean isEncode) {
StringBuilder sb = new StringBuilder();
sb.append(key);
sb.append("=");
if (isEncode) {
try {
sb.append(URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
sb.append(value);
}
} else {
sb.append(value);
}
return sb.toString();
}
/**
* 对支付参数信息进行签名
*
* @param map 待签名授权信息
* @return
*/
public static String getSign(Map<String, String> map) {
List<String> keys = new ArrayList<String>(map.keySet());
// key排序
Collections.sort(keys);
StringBuilder authInfo = new StringBuilder();
for (int i = 0; i < keys.size() - 1; i++) {
String key = keys.get(i);
String value = map.get(key);
authInfo.append(buildKeyValue(key, value, false));
authInfo.append("&");
}
String tailKey = keys.get(keys.size() - 1);
String tailValue = map.get(tailKey);
authInfo.append(buildKeyValue(tailKey, tailValue, false));
boolean rsa2 = ALiPayConfig.sign_type.equals("RSA2");
String oriSign = AliSignUtils.sign(authInfo.toString(), ALiPayConfig.private_key, rsa2);
String encodedSign = "";
try {
encodedSign = URLEncoder.encode(oriSign, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "sign=" + encodedSign;
}
/**
* 要求外部订单号必须唯一。
*
* @return
*/
private static String getOutTradeNo() {
SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
Date date = new Date();
String key = format.format(date);
Random r = new Random();
key = key + r.nextInt();
key = key.substring(0, 15);
return key;
}
}
ALiPayConfig:
package com.hakj.express.utils.alipayutils;
/**
* Description:
* Author:THP
* Date:2018/05/09 15:38
*/
public class ALiPayConfig {
// 合作身份者ID,以2088开头由16位纯数字组成的字符串
public static String partner = "2088*********4";
// 6.请求网关地址
public static String URL = "https://openapi.alipay.com/gateway.do";
public static String service = "mobile.securitypay.pay";//固定值
public static String seller_id = "*******@163.com";
//私钥
public static String private_key = "***";
// 商户的公钥钥
public static String public_key = "***";
// 支付宝的公钥,无需修改该值(不要删除也不要修改,在接收通知的时候需要进行签名认证)
public static String ali_public_key = "***";
// 字符编码格式 目前支持 gbk 或 utf-8
public static String input_charset = "UTF-8";
// 签名方式 不需修改
public static String sign_type = "RSA2";
//APPID
public static String APPID = "***";
//合作伙伴身份(PID)
public static String PID = "***";
//支付宝回调地址 要与蚂蚁开放平台回调地址一致,否则不回调
public static String notify_url = "http://****/ali-pay/callback";
// 8.返回格式
public static String FORMAT = "json";
}
ALiPayUtil:
package com.hakj.express.utils.alipayutils;
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.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.hakj.express.service.impl.UserConfigServiceimpl;
import lombok.extern.log4j.Log4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.Map;
/**
* Description:支付宝支付工具类
* Author:THP
* Date:2018/05/07 15:17
*/
@Log4j
@Component
public class ALiPayUtil {
//业务服务
@Resource
private UserConfigServiceimpl configService;
/**
* 支付宝下单
*
* @param goodsInfo 商品信息
* @param goodsName 商品名
* @param amount 金额
* @param orderNumber 订单编号
*/
@Deprecated
public void pay(String goodsInfo, String goodsName, String amount, String orderNumber) {
//实例化客户端
// AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(ALiPayConfig.URL, ALiPayConfig.APPID, ALiPayConfig.private_key, ALiPayConfig.FORMAT, ALiPayConfig.input_charset, ALiPayConfig.ali_public_key, ALiPayConfig.sign_type);
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody(goodsInfo);//商品信息
model.setSubject(goodsName);// 商品名称
// model.setOutTradeNo(outtradeno);
model.setOutTradeNo(orderNumber); // 商户订单号
model.setTimeoutExpress("30m");//支付超时时间
model.setTotalAmount(amount);// 支付金额
// model.setProductCode("QUICK_MSECURITY_PAY");// 销售产品码
request.setBizModel(model);
request.setNotifyUrl(ALiPayConfig.notify_url);// 回调地址
try {
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
System.out.println(response.getBody());//就是orderString 可以直接给客户端请求,无需再做处理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
}
/**
* 支付宝退款
*
* @param out_trade_no 订单支付时传入的商户订单号,不能和 trade_no同时为空。
* @param trade_no 支付宝交易号,和商户订单号不能同时为空
* @param refund_amount 需要退款的金额,该金额不能大于订单金额,单位为元,支持两位小数
*/
public String alipayRefundRequest(String out_trade_no, String trade_no, double refund_amount) {
// 发送请求
String strResponse = null;
AlipayClient alipayClient = new DefaultAlipayClient(ALiPayConfig.URL, ALiPayConfig.APPID, ALiPayConfig.private_key, ALiPayConfig.FORMAT, ALiPayConfig.input_charset, ALiPayConfig.ali_public_key, ALiPayConfig.sign_type);
try {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
// AlipayRefundInfo alidata = new AlipayRefundInfo();
// alidata.setOut_trade_no(out_trade_no);
// alidata.setRefund_amount(refund_amount);
// alidata.setTrade_no(trade_no);
DecimalFormat df = new DecimalFormat("0.00");
refund_amount *= configService.queryRate();
String sa = df.format(refund_amount);
refund_amount = Double.parseDouble(sa);
request.setBizContent("{" +
"\"out_trade_no\":\"" + out_trade_no + "\"," +
"\"trade_no\":\"" + trade_no + "\"," +
"\"refund_amount\":" + refund_amount + "," +
"\"refund_reason\":\"正常退款\"," +
"\"out_request_no\":\"HZ01RF001\"" +
" }");
AlipayTradeRefundResponse response = alipayClient.execute(request);
strResponse = response.getCode();
if ("10000".equals(response.getCode())) {
strResponse = "退款成功";
} else {
strResponse = response.getSubMsg();
}
} catch (Exception e) {
log.error("alipayRefundRequest", e);
}
return strResponse;
}
/**
* 余额提现
*
* @param orderNo 商户订单号
* @param aliCode 支付宝账号
* @param name 收款人姓名
* @param amount 金额
* @return 成功失败
*/
public AlipayFundTransToaccountTransferResponse withDrawDeposit(String orderNo, String aliCode, String name, String amount) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(ALiPayConfig.URL, ALiPayConfig.APPID, ALiPayConfig.private_key, ALiPayConfig.FORMAT, ALiPayConfig.input_charset, ALiPayConfig.ali_public_key, ALiPayConfig.sign_type);
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
DecimalFormat df = new DecimalFormat("#.00");
double am = Double.parseDouble(amount);
am *= configService.queryRate();
amount = df.format(am);
request.setBizContent("{" +
"\"out_biz_no\":\"" + orderNo + "\"," +
"\"payee_type\":\"ALIPAY_LOGONID\"," +
"\"payee_account\":\"" + aliCode + "\"," +
"\"amount\":\"" + amount + "\"," +
"\"payer_show_name\":\"展示的短内容\"," +
"\"payee_real_name\":\"" + name + "\"," +
"\"remark\":\"这里是内容\"" +
"}");
AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
// if (response.isSuccess()) {
// System.out.println("成功");
// } else {
// System.out.println("失败");
// }
return response;
}
/**
* 查支付宝订单信息
*
* @param orderNo 商家订单号
* @param AliOrderNo 支付宝订单号
* @throws AlipayApiException
*/
public String queryAliOrderInfo(String orderNo, String AliOrderNo) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(ALiPayConfig.URL, ALiPayConfig.APPID, ALiPayConfig.private_key, ALiPayConfig.FORMAT, ALiPayConfig.input_charset, ALiPayConfig.ali_public_key, ALiPayConfig.sign_type);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{" +
"\"out_trade_no\":\"" + orderNo + "\"," +
"\"trade_no\":\"" + AliOrderNo + "\"" +
" }");
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return response.toString();
}
/**
* 签名
*
* @param price 价格
* @param openid 商户订单号
* @param orderName 订单名
* @param orderType 订单类型 (业务需要)
* @return 订单签名后的数据
*/
public String getOrderInfo(double price, String openid, String orderName, String orderType) {
DecimalFormat df = new DecimalFormat("#.00");
price *= configService.queryRate();
String sa = df.format(price);
price = Double.parseDouble(sa);
Map<String, String> params = ALiOrderInfoUtil2.buildOrderParamMap(price, openid, orderName, orderType);
String orderParam = ALiOrderInfoUtil2.buildOrderParam(params);
String sign = ALiOrderInfoUtil2.getSign(params);
return orderParam + "&" + sign;
}
}
AliRSA:
package com.hakj.express.utils.alipayutils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* Description:
* Author:THP
* Date:2018/05/17 10:09
*/
public class AliRSA {
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
/**
* RSA签名
*
* @param content 待签名数据
* @param privateKey 商户私钥
* @param input_charset 编码格式
* @return 签名值
*/
public static String sign(String content, String privateKey, String input_charset) {
try {
byte[] decode = new Base64().decode(privateKey);
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decode);
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(input_charset));
byte[] signed = signature.sign();
return new String(new Base64().encode(signed));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* RSA验签名检查
*
* @param content 待签名数据
* @param sign 签名值
* @param ali_public_key 支付宝公钥
* @param input_charset 编码格式
* @return 布尔值
*/
public static boolean verify(String content, String sign, String ali_public_key, String input_charset) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = new Base64().decode(ali_public_key);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initVerify(pubKey);
signature.update(content.getBytes(input_charset));
boolean bverify = signature.verify(new Base64().decode(sign));
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 解密
*
* @param content 密文
* @param private_key 商户私钥
* @param input_charset 编码格式
* @return 解密后的字符串
*/
public static String decrypt(String content, String private_key, String input_charset) throws Exception {
PrivateKey prikey = getPrivateKey(private_key);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, prikey);
InputStream ins = new ByteArrayInputStream(new Base64().decode(content));
ByteArrayOutputStream writer = new ByteArrayOutputStream();
//rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密
byte[] buf = new byte[128];
int bufl;
while ((bufl = ins.read(buf)) != -1) {
byte[] block = null;
if (buf.length == bufl) {
block = buf;
} else {
block = new byte[bufl];
for (int i = 0; i < bufl; i++) {
block[i] = buf[i];
}
}
writer.write(cipher.doFinal(block));
}
return new String(writer.toByteArray(), input_charset);
}
/**
* 得到私钥
*
* @param key 密钥字符串(经过base64编码)
* @throws Exception
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = new Base64().decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
}
AliSignUtils:
package com.hakj.express.utils.alipayutils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* Description:
* Author:THP
* Date:2018/05/17 13:22
*/
public class AliSignUtils {
private static final String ALGORITHM = "RSA";
private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";
private static final String DEFAULT_CHARSET = "UTF-8";
private static String getAlgorithms(boolean rsa2) {
return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;
}
public static String sign(String content, String privateKey, boolean rsa2) {
try {
Security.addProvider(new BouncyCastleProvider());
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, "BC");
PrivateKey priKey = keyFactory.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(getAlgorithms(rsa2));
signature.initSign(priKey);
signature.update(content.getBytes(DEFAULT_CHARSET));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Base64:
package com.hakj.express.utils.alipayutils;
/**
* Description:Base64加密
* Author:THP
* Date:2018/05/17 13:23
*/
public class Base64 {
private static final int BASELENGTH = 128;
private static final int LOOKUPLENGTH = 64;
private static final int TWENTYFOURBITGROUP = 24;
private static final int EIGHTBIT = 8;
private static final int SIXTEENBIT = 16;
private static final int FOURBYTE = 4;
private static final int SIGN = -128;
private static char PAD = '=';
private static byte[] base64Alphabet = new byte[BASELENGTH];
private static char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static {
for (int i = 0; i < BASELENGTH; ++i) {
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--) {
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--) {
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--) {
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = (char) '+';
lookUpBase64Alphabet[63] = (char) '/';
}
private static boolean isWhiteSpace(char octect) {
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
}
private static boolean isPad(char octect) {
return (octect == PAD);
}
private static boolean isData(char octect) {
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
}
/**
* Encodes hex octects into Base64
*
* @param binaryData
* Array containing binaryData
* @return Encoded Base64 array
*/
public static String encode(byte[] binaryData) {
if (binaryData == null) {
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0) {
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
: numberTriplets;
char encodedData[] = null;
encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
for (int i = 0; i < numberTriplets; i++) {
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
: (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
// form integral number of 6-bit groups
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
/**
* Decodes Base64 data into octects
*
* @param encoded
* string containing Base64 data
* @return Array containind decoded data.
*/
public static byte[] decode(String encoded) {
if (encoded == null) {
return null;
}
char[] base64Data = encoded.toCharArray();
// remove white spaces
int len = removeWhiteSpace(base64Data);
if (len % FOURBYTE != 0) {
return null;// should be divisible by four
}
int numberQuadruple = (len / FOURBYTE);
if (numberQuadruple == 0) {
return new byte[0];
}
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
int i = 0;
int encodedIndex = 0;
int dataIndex = 0;
decodedData = new byte[(numberQuadruple) * 3];
for (; i < numberQuadruple - 1; i++) {
if (!isData((d1 = base64Data[dataIndex++]))
|| !isData((d2 = base64Data[dataIndex++]))
|| !isData((d3 = base64Data[dataIndex++]))
|| !isData((d4 = base64Data[dataIndex++]))) {
return null;
}// if found "no data" just return null
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
if (!isData((d1 = base64Data[dataIndex++]))
|| !isData((d2 = base64Data[dataIndex++]))) {
return null;// if found "no data" just return null
}
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
d3 = base64Data[dataIndex++];
d4 = base64Data[dataIndex++];
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
if (isPad(d3) && isPad(d4)) {
if ((b2 & 0xf) != 0)// last 4 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 1];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
return tmp;
} else if (!isPad(d3) && isPad(d4)) {
b3 = base64Alphabet[d3];
if ((b3 & 0x3) != 0)// last 2 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 2];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
return tmp;
} else {
return null;
}
} else { // No PAD e.g 3cQl
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
return decodedData;
}
/**
* remove WhiteSpace from MIME containing encoded Base64 data.
*
* @param data
* the byte array of base64 data (with WS)
* @return the new length
*/
private static int removeWhiteSpace(char[] data) {
if (data == null) {
return 0;
}
// count characters that's not whitespace
int newSize = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
if (!isWhiteSpace(data[i])) {
data[newSize++] = data[i];
}
}
return newSize;
}
}
回调
payCallback:
package com.hakj.express.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.hakj.express.utils.OrderUtil;
import com.hakj.express.utils.alipayutils.ALiPayConfig;
import com.hakj.express.utils.alipayutils.ALiPayUtil;
import lombok.Data;
import lombok.extern.log4j.Log4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* Description: 支付宝支付处理接口
* Author:THP
* Date:2018/05/07 15:27
*/
@Log4j
@RestController
public class ALiPayController {
@Resource
private ALiPayUtil aLiPayUtil;
@Resource
private OrderUtil orderUtil;
/**
* 支付回调
*
* @param request
* @throws Exception
*/
@RequestMapping("/ali-pay/callback")
public String payCallback(HttpServletRequest request) throws Exception {
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//封装必须参数
String out_trade_no = request.getParameter("out_trade_no"); // 商户订单号
System.err.println("out_trade_no==================================" + out_trade_no);
String orderType = request.getParameter("body"); // 订单内容
System.out.println("orderType==================================" + orderType);
//交易金额
String totalAmount = request.getParameter("total_amount");
//支付宝订单号
String trade_no = request.getParameter("trade_no");
System.err.println("trade_no=================================" + trade_no);
String tradeStatus = request.getParameter("trade_status"); // 交易状态
System.err.println("tradeStatus=================================" + tradeStatus);
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
boolean flag = AlipaySignature.rsaCheckV1(params, ALiPayConfig.ali_public_key, ALiPayConfig.input_charset, ALiPayConfig.sign_type);
//对验签进行处理
if (flag) {
// 验签通过
if (tradeStatus.equals("TRADE_SUCCESS")) {
// 只处理支付成功的订单:修改交易表状态,支付成功
//自己的业务逻辑操作
orderUtil.upOrderState(out_trade_no, orderType, Double.parseDouble(totalAmount), 3, trade_no);
return "success";
}
}
return "";
}
@PostMapping("/quer-order/alipay")
public String queryAliOrder(String orderNo, String aliOrderNo) throws AlipayApiException {
return aLiPayUtil.queryAliOrderInfo(orderNo, aliOrderNo);
}
/**
* 手动退款
*
* @param orderNo 商户订单号
* @param aliOrderNo 支付宝订单号
* @param amount 金额
* @return 结果
*/
@PostMapping("/ali/back")
public String aliback(String orderNo, String aliOrderNo, double amount) {
return aLiPayUtil.alipayRefundRequest(orderNo, aliOrderNo, amount);
}
}
收到支付宝回调,并处理业务成功,一定要返回字符串:success
收到支付宝回调,并处理业务成功,一定要返回字符串:success
收到支付宝回调,并处理业务成功,一定要返回字符串:success
否则支付宝会认为你没收到回调或业务处理失败,每隔一段时间就发一次,具体间隔多少时间看官方文档
好了,就这么多了,应该能应对大部分需求了
至于调试,发布到远程服务器上调试不方便,可以使用内网穿透工具natapp(有免费和收费的,免费的就够用了),然后对着官方文档,足以解决90%的问题,还是不行,Google一下
希望对各位有所帮助,如有不足之处,欢迎点出!