最近在做一些支付宝相关的支付后台实现,主要用的是Spring boot框架。因为支付宝老版的文档已经十分不详细(新版的也说的不清楚),所以我踩了无数坑,决定写篇博客来帮助后来人。
关于新版支付宝支付,需要APPID,也就是说必须在支付宝那儿建立应用并上线,还必须签约APP支付开通这个服务才能用。
但是本次我使用的是老版APP支付接口,所以并不需要APPID,只要签约支付宝开通APP支付服务就行。
关于流程,前端使用的是DCcloud的H5框架,集成了支付宝客户端SDK,整个流程如下:
用户点击支付按钮->客户端调用自己服务端API->自己服务端生成支付orderinfo(字符串)返回客户端->客户端拿orderinfo调用支付宝客户端SDK->SDK自启动支付宝APP完成支付->支付宝服务端给自己服务端发送异步通知->用户在异步通知验证支付成功后对自己数据库进行操作。
第一步:生成私钥公钥。这个可以用工具生成,需要去下载一键生成RSA密钥工具。下载地址:https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.pohUoc&treeId=291&articleId=106097&docType=1
第二步:在支付宝网站上上传你的公钥:

为防万一,请将开放平台密钥,mapi网关产品密钥,老版wap支付密钥全部配上你自己生成的公钥。
支付宝异步通知你的时候,请使用mapi网关产品密钥的支付宝公钥进行验证。
第三步:服务端如何生成orderinfo?不多说直接上代码:
-
package com.ibm.callcenter.controller; -
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.internal.util.AlipaySignature; -
import com.alipay.api.request.AlipayTradeAppPayRequest; -
import com.alipay.api.response.AlipayTradeAppPayResponse; -
import org.springframework.stereotype.Controller; -
import org.springframework.ui.Model; -
import org.springframework.web.bind.annotation.RequestMapping; -
import org.springframework.web.bind.annotation.RequestMethod; -
import org.springframework.web.bind.annotation.RequestParam; -
import org.springframework.web.bind.annotation.ResponseBody; -
import com.ibm.callcenter.util.SignUtils; -
import javax.servlet.http.HttpServletRequest; -
import javax.servlet.http.HttpServletResponse; -
import java.io.UnsupportedEncodingException; -
import java.net.URLEncoder; -
import java.text.SimpleDateFormat; -
import java.util.Date; -
import java.util.HashMap; -
import java.util.Iterator; -
import java.util.Map; -
@Controller -
@RequestMapping(value="/alipay") -
@ResponseBody -
public class test { -
@RequestMapping(value = "/pay") -
public String pay(//@RequestParam("fee") String total_fee, -
HttpServletResponse response1) { -
Date now = new Date(); -
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式 -
String hehe = dateFormat.format( now ); -
System.out.println(hehe+"test123"); -
String out_strade_no=hehe+"wentest"; //自己写的订单编号 -
String private_key="写上你自己生成的与上传公钥相对应的私钥"; -
String partner="写上你自己的UID(PID)"; -
String seller_id="写上你支付宝收款的账号"; -
String body="aiyoubucuo"; -
String notify_url="写上你异步通知的接口地址,切记需要是外网地址"; -
String show_url="https://www.baidu.com/"; //支付宝同步跳转地址 -
String total_fee="0.01"; //支付金额 -
String subject="test"; -
String order="service=\"mobile.securitypay.pay\"&partner=\""+partner+"\"&_input_charset=\"UTF-8\"&out_trade_no=\""+out_strade_no+"\"&subject=\""+subject+"\"&payment_type=\"1\"&seller_id=\""+seller_id+"\"&total_fee=\""+total_fee+"\"&body=\""+body+"\"&it_b_pay=\"1d\"¬ify_url=\""+notify_url+"\"&show_url=\""+show_url+"\""; -
String sign=SignUtils.sign(order,private_key,false); -
try { -
// 仅需对sign 做URL编码 -
sign = URLEncoder.encode(sign, "UTF-8"); -
} catch (UnsupportedEncodingException e) { -
e.printStackTrace(); -
} -
final String result = order + "&sign=\"" + sign + "\"&" -
+ getSignType(); -
System.err.println("123"+result); -
return result; -
} -
public String getSignType() { -
return "sign_type=\"RSA\""; -
} -
/** -
* 异步通知付款状态的Controller -
* @param request -
* @param response -
* @return -
*/ -
@SuppressWarnings("rawtypes") -
@RequestMapping(value="/notify",method = RequestMethod.POST ) -
public String notify(HttpServletRequest request, -
HttpServletResponse response) throws AlipayApiException { -
Map<String,String> params = new HashMap<String,String>(); -
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] + ","; -
} -
params.put(name, valueStr); -
} -
String CHARSET = "UTF-8"; -
String ALIPAY_PUBLIC_KEY="写上你支付宝生成的公钥"; -
String tradeNo = request.getParameter("out_trade_no"); -
String tradeStatus = request.getParameter("trade_status"); -
//String notifyId = request.getParameter("notify_id"); -
//System.err.println(params.toString()); -
boolean flag = AlipaySignature.rsaCheckV1(params,ALIPAY_PUBLIC_KEY, CHARSET,"RSA"); -
if(flag){//验证成功 -
if(tradeStatus.equals("TRADE_FINISHED") || tradeStatus.equals("TRADE_SUCCESS")) { -
//要写的逻辑。自己按自己的要求写 -
// log.error("ok......."); -
System.err.println(">>>>>充值成功" + tradeNo); -
} -
return "success"; -
}else{//验证失败 -
System.out.println("fail yanzheng"); -
return "web/pay/fail"; -
} -
} -
}
把生成签名的两个需要依赖的类也给大家:
-
package com.ibm.callcenter.util; -
import java.security.KeyFactory; -
import java.security.PrivateKey; -
import java.security.spec.PKCS8EncodedKeySpec; -
public class SignUtils { -
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 { -
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( -
Base64.decode(privateKey)); -
KeyFactory keyf = KeyFactory.getInstance(ALGORITHM); -
PrivateKey priKey = keyf.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; -
} -
}
以及这个:
-
package com.ibm.callcenter.util; -
public final 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; -
} -
}
代码与业务逻辑应该很清楚了。有不懂的同学可以评论问我,我再做补充。
450

被折叠的 条评论
为什么被折叠?



