最近有机会接触了微信及支付宝APP支付相关后台功能,特此记录一下开发步骤,以便以后查阅,也希望对各位理解上有所帮助
支付宝及微信支付流程大致相同,主要开发步骤为:
- 在平台申请商户账户(微信商户平台、微信开发者平台,蚂蚁金服开放平台),主要工作内容为提交商户相关资料,配置管理员账户,配置及获取开发所需公钥私钥商户号APPID等
- 开发统一下单(预支付)接口。微信支付宝支付流程均有此步骤,app客户端发起支付,后台根据相关应用配置及支付的商户订单号、金额等内容进行签名调用平台统一下单接口,生成预支付ID,后台再将返回结果进行签名处理返回给app客户端。因为私钥不会保存在app客户端,所以加签及验签工作均在服务端完成。
- app客户端根据统一下单接口的返回进行支付调用,调用微信或支付宝进行支付操作,微信及支付直接向平台发送支付请求。
- 微信支付宝在支付成功后,根据统一下单中的配置的回调接口进行回调,商户服务端后台对回调权限进行放开,调用后验签,处理订单。
附上官方图,便于各位理解
支付宝流程
image.png
微信流程
image.png支付宝支付相关代码
支付宝配置
#支付宝支付
#支付宝网关(固定)
alipay.GATE_WAY_URL=https://openapi.alipay.com/gateway.do
#应用ID
alipay.APP_ID=在蚂蚁开发平台申请的应用ID
#应用私钥
#alipay.APP_PRIVATE_KEY=就是使用RSA签名验签工具生成的应用私钥,其中生成的应用公钥需配置在平台上
#支付宝公钥
alipay.ALIPAY_PUBLIC_KEY=查看平台配置
#回调接口
alipay.NOTIFY_URL=暴露给支付宝的回调接口
统一下单,在支付宝中并没有这个统一下单的说法,但是是一样的,相当于请求支付宝进行预支付,完成签名操作
// EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
AlipayClient alipayClient = AlipayUtil.getAlipayClient();
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody("订单数据");
model.setSubject("一个主题没有特殊意义");
//订单号
model.setOutTradeNo("商户订单号");
//该笔订单允许的最晚付款时间 m分钟
model.setTimeoutExpress("30m");
//总金额
model.setTotalAmount("订单金额");
//固定值 QUICK_MSECURITY_PAY
model.setProductCode(AlipayUtil.PRODUCT_CODE);
request.setBizModel(model);
//回调接口
request.setNotifyUrl(AlipayUtil.NOTIFY_URL);
try {
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
Map<String, String> map = new HashMap<String, String>();
//将预支付结果直接返回前端,客户端可以使用该返回直接调用支付
map.put("alipay", response.getBody());
return map;
} catch (AlipayApiException e) {
throw new ServiceException(e.getMessage());
}
客户端获取到返回后,直接使用返回数据向通过支付宝客户端向支付宝服务端发起真正支付请求,支付宝服务端在支付成功后调用回调接口,商户服务端在回调接口做订单成功处理
回调接口,可以多写日志方便查找问题
/**
* 支付宝回调接口,放开权限
*
* @param request
* @return
*/
@PostMapping("/alipayNotify")
public Map<String, Object> alipayNotify(HttpServletRequest request) {
logger.info("支付宝支付异步通知开始==============》");
try {
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] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
//收到的参数、支付宝公钥、字符集、签名加密类型
boolean flag = AlipaySignature
.rsaCheckV1(params, AlipayUtil.ALIPAY_PUBLIC_KEY, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2);
if (!flag) {
logger.error("验签失败");
return null;
}
/**
* 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
* 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
* 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
* 4、验证app_id是否为该商户本身。
* 上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。
* 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。
* 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
*/
//商户订单号
String orderCode = params.get("out_trade_no");
String appId = params.get("app_id");
//订单金额
String totalAmount = params.get("total_amount");
//支付宝交易凭证号
//String tradeNo = params.get("trade_no");
//回传参数
//String passbackParams = params.get("passback_params");
if (StringUtils.isEmpty(orderCode)) {
logger.error("未能获取到回调订单ID");
return null;
}
if (!AlipayUtil.APP_ID.equals(appId)) {
logger.error("appId错误");
return null;
}
TransportOrder order = transportOrderService.findByOrderCode(orderCode);
if (order == null) {
logger.error("订单不存在");
return null;
}
if (!(order.getInformationFee().compareTo(new BigDecimal(totalAmount)) == 0)) {
logger.error("订单金额错误");
return null;
}
//此处为校验通过,进行真正成功订单处理
transportOrderService.paySuccess(orderCode);
logger.info("支付宝支付回调,订单: {} 处理成功!", orderCode);
} catch (AlipayApiException e) {
logger.error("支付宝支付回调发生异常,信息: {} ", e.getMessage());
}
logger.info("支付宝支付异步通知结束==============》");
return null;
}
支付宝工具类
/**
* Created by wangqichang on 2018/12/24.
*/
public class AlipayUtil {
public static final String GATE_WAY_URL = PropertyUtils.get("alipay.GATE_WAY_URL");
public static final String APP_PRIVATE_KEY = PropertyUtils.get("alipay.APP_PRIVATE_KEY");
public static final String APP_ID = PropertyUtils.get("alipay.APP_ID");
public static final String ALIPAY_PUBLIC_KEY = PropertyUtils.get("alipay.ALIPAY_PUBLIC_KEY");
public static final String NOTIFY_URL = PropertyUtils.get("alipay.NOTIFY_URL");
public static final String PRODUCT_CODE = "QUICK_MSECURITY_PAY";
private static AlipayClient alipayClient = null;
//支付宝sdk客户端类,只需要创建一次即可
public static synchronized AlipayClient getAlipayClient() {
if (alipayClient == null) {
alipayClient = new DefaultAlipayClient(AlipayUtil.GATE_WAY_URL, AlipayUtil.APP_ID, AlipayUtil.APP_PRIVATE_KEY,
AlipayConstants.FORMAT_JSON, AlipayConstants.CHARSET_UTF8, AlipayUtil.ALIPAY_PUBLIC_KEY,
AlipayConstants.SIGN_TYPE_RSA2);
}
return alipayClient;
}
}
以上就是支付宝APP支付服务端需要配置和写入的代码,经过测试可以正常运行。本文不对客户端(ios 安卓)讲解。
微信支付相关代码
微信支付配置,注意这些配置有些在微信开放平台,有些在商户平台找
#微信支付
#微信开放平台审核通过的应用APPID
tenpay.unifiedorder=https://api.mch.weixin.qq.com/pay/unifiedorder
tenpay.orderquery=https://api.mch.weixin.qq.com/pay/orderquery
tenpay.appid=APPID这个在商户平台
#商户号
tenpay.mch_id=商户号商户平台
#AppSecret
tenpay.AppSecret=开发平台APP秘钥
#API密钥
tenpay.PrivateKey=商户平台API秘钥
#微信服务器主动通知商户服务器里指定的页面http/https路径。建议商户使用https
tentpay_notify_url=商户服务端回调接口
#本应用IP
tenpay.spbillCreateIp=商户服务端IP地址(公网)
统一下单
//加签验签的参数需要排序
SortedMap<String, String> params = new TreeMap<String, String>();
//应用ID
params.put("appid", WXPayConstants.APPID);
//商户号
params.put("mch_id", WXPayConstants.MCHID);
//随机字符串
params.put("nonce_str", WXPayConstants.getRandomString(32));
//签名类型,默认MD5
// params.put("sign_type", "");
//商品描述
params.put("body", "一个描述,随便写");
//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
params.put("attach", "附加信息,可以放些回调需要用到的参数");
//商户订单号
params.put("out_trade_no", "商户订单号");
//总金额,微信以分做单位
params.put("total_fee", String.valueOf(order.getInformationFee().multiply(new BigDecimal(100)).intValue()));
//终端ip
params.put("spbill_create_ip", WXPayConstants.SPBILLCREATEIP);
//通知地址
params.put("notify_url", WXPayConstants.NOTIFYURL);
//交易类型
params.put("trade_type", "APP");
String sign = WXSignUtil.createSign(params);
params.put("sign", sign);
try {
String xml = WXPayUtil.mapToXml(params);
String result = HttpClientUtil.postXML(WXPayConstants.UNIFIEDORDER, xml);
System.out.println(result);
Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
if (resultMap.get("return_code") != null && WXPayConstants.SUCCESS.equals(resultMap.get("return_code"))) {
//将app支付所需参数返回给商户客户端(ios 安卓),前端需要这些参数进行支付调用
SortedMap<String, String> data = new TreeMap<String, String>();
data.put("appid", WXPayConstants.APPID);
data.put("partnerid", WXPayConstants.MCHID);
data.put("prepayid", resultMap.get("prepay_id"));
data.put("package", WXPayConstants.PACKAGE);
data.put("noncestr", WXPayConstants.getRandomString(32));
data.put("timestamp", System.currentTimeMillis() / 1000 + "");
String sign1 = WXSignUtil.createSign(data);
data.put("sign", sign1);
return data;
}
throw new ServiceException(resultMap.get("return_msg"));
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
微信支付回调
/**
* 微信支付回调
*
* @param
* @return
*/
@PostMapping("/tenpayNotify")
public String tenpayNotify(HttpServletRequest request) {
logger.info("微信支付异步通知开始==============》");
try {
BufferedReader br = request.getReader();
String xml = "";
String line = br.readLine();
while (org.apache.commons.lang.StringUtils.isNotEmpty(line)) {
xml += new String(line.getBytes());
line = br.readLine();
}
if (StringUtils.isEmpty(xml)) {
logger.error("未解析到回调参数");
return null;
} else {
logger.info("回调参数xml :{} ", xml);
}
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
if (resultMap.get("return_code") != null && WXPayConstants.SUCCESS.equals(resultMap.get("return_code"))
&& WXPayConstants.SUCCESS.equals(resultMap.get("result_code"))) {
logger.info("微信支付回调返回成功,信息:{}", resultMap);
/**
* 验签
* 查询订单支付状态
* 修改订单状态
* 通知微信收到通知
*
*/
String sign = resultMap.get("sign");
SortedMap<String, String> map = new TreeMap<String, String>(resultMap);
String sign1 = WXSignUtil.createSign(map);
if (!sign.equals(sign1)) {
logger.error("微信支付回调验签失败");
return null;
}
//商户订单号
String orderCode = resultMap.get("out_trade_no");
//微信支付订单号
// resultMap.get("transaction_id");
//订单附件
// resultMap.get("attach");
if (StringUtils.isEmpty(orderCode)) {
logger.error("未能获取到订单号");
return null;
}
//此处做业务成功处理
transportOrderService.paySuccess(orderCode);
logger.info("微信支付回调,订单:{} 处理成功!", orderCode);
//此处向微信返回成功收到通知
HashMap<String, String> wechatReturnMap = new HashMap<String, String>();
wechatReturnMap.put("return_code", WXPayConstants.SUCCESS);
wechatReturnMap.put("return_msg", WXPayConstants.OK);
String returnXml = WXPayUtil.mapToXml(wechatReturnMap);
return returnXml;
} else {
logger.error("微信支付回调返回失败,信息:{}", resultMap.toString());
}
} catch (Exception e) {
logger.error("微信支付回调处理失败, 信息:" + e.getMessage());
} finally {
logger.info("微信支付异步通知结束==============》");
}
return null;
}
微信支付相关工具类,这些工具类均可在微信官方提供的sdk中找到,可以依据个人习惯稍作更改,也可以直接使用
xml解析等 WXPayUtil
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class WXPayUtil {
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
return XmlUtil.parseXml(strXML);
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名类型
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, WXPayConstants.SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (WXPayConstants.SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日志
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 获取当前时间戳,单位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
* @return
*/
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}
签名工具类WXSignUtil
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
public class WXSignUtil {
/**
* 生成签名参数
*
* @param param
* @return
*/
public static String createSign(SortedMap<String, String> param) {
StringBuffer sb = new StringBuffer();
Set es = param.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
// 拼接
sb.append("key=" + WXPayConstants.PRIVATE_KEY);
String sign = null;
try {
sign = MD5(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
return sign;
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
}
支付常量类
import java.util.Random;
/**
* 常量
*/
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
//微信支付商户开通后 微信会提供appid和appsecret和商户号partner
public static final String APPID = PropertyUtils.get("tenpay.appid");//在微信开发平台登记的app应用
public static final String APPSECRET = PropertyUtils.get("tenpay.AppSecret");
public static final String PRIVATE_KEY = PropertyUtils.get("tenpay.PrivateKey");
public static final String MCHID = PropertyUtils.get("tenpay.mch_id");//商户号
public static final String SPBILLCREATEIP = PropertyUtils.get("tenpay.spbillCreateIp");//
public static final String UNIFIEDORDER = PropertyUtils.get("tenpay.unifiedorder");
public static final String ORDERQUERY = PropertyUtils.get("tenpay.orderquery");
//微信支付成功后通知地址 必须要求80端口并且地址不能带参数
public static final String NOTIFYURL = PropertyUtils.get("tentpay_notify_url");
public static final String PACKAGE = "Sign=WXPay";
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
public static final String FAIL = "FAIL";
public static final String SUCCESS = "SUCCESS";
public static final String OK = "OK";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
public static final String REPORT_URL_SUFFIX = "/payitil/report";
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
// sandbox
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
/**
* 产生随机String
* @param length
* @return String
*/
public static String getRandomString(int length){
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for(int i=0; i<length; i++){
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
}
以上代码均经过测试通过,可以直接移植使用。
建议结合官方文档进行理解,本文仅做辅助了解,特别是官方做了更新的情况,所以看官请注意本文发布时间,太久最好不用
总结一下,在官方文档中,支付宝做的比微信好,更加清晰简洁,也为开发者提供了客户端类,封装大部分加签验签工作,从代码中也看的出来。
官方文档记得多看
网友评论