项目有转账到个人账户的需求.所以支付宝秘钥要用公钥证书的方式,如果不用转账可以用普通的公私钥就可以了.刚开始支付宝的转账文档里面没有用到证书,是错误的,加上习惯看文档一般都直奔目的地.结果接口就是调试不好.后来问了客服,让参考接入指南,这里面的介绍和代码是正确的.参考地址:接入指南 | 网页&移动应用.过了2天回头看文档已经都改正;微信的v3支付就是个坑.感觉文档写的是真菜.我喜欢支付宝这样的直接告诉你这么做,代码怎么写.微信就喜欢让你猜.找客服问个验签的问题,客服直接甩出文档说代码你自己实现.文档能看通谁还找客服.等个客服好点的十几分钟.有次有快1小时了.最后实在验签不了放那等第二天吧.第二天一早想到自动更新证书里面可以获取证书.用这个试试.毕竟支付的时候验签就ton过了,没理由回调的时候验签不通过.一式果然成了.
支付宝
支付宝api还比较清楚.如果用到转账.余额查询等敏感接口需要使用公钥证书的加密方式.公钥证书和普通加密方式只能同时存在一种.普通加密方式切换到公钥正式会有个过渡期.两者可共存.新版api退款不用在设置notifyurl,会使用支付时传入的地址,在支付回调的时候分别处理下就可以了.下面代码都是使用公钥正式加密方式.证书可以用提供的工具生成.不做介绍.
1.创建证书client
建议直接复制代码.手写可能会遗漏,亲身经历.证书地址为磁盘绝对路径.用/
private AlipayClient getAlipayClient() throws AlipayApiException {
/** 初始化 **/
CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
/** 支付宝网关 **/
certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
/** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/
certAlipayRequest.setAppId(appId);
/** 应用私钥, 如何获取请参考:https://opensupport.alipay
* .com/support/helpcenter/207/201602471154?ant_source=antsupport **/
certAlipayRequest.setPrivateKey(privateKey);
/** 应用公钥证书路径,下载后保存位置的绝对路径 **/
certAlipayRequest.setCertPath(appPublicCert);
/** 支付宝公钥证书路径,下载后保存位置的绝对路径 **/
certAlipayRequest.setAlipayPublicCertPath(alipayPublicCert);
/** 支付宝根证书路径,下载后保存位置的绝对路径 **/
certAlipayRequest.setRootCertPath(aliRootCert);
/** 设置签名类型 **/
certAlipayRequest.setSignType("RSA2");
/** 设置请求格式,固定值json **/
certAlipayRequest.setFormat("json");
/** 设置编码格式 **/
certAlipayRequest.setCharset("UTF-8");
return new DefaultAlipayClient(certAlipayRequest);
}
2.支付
虽然用了证书.支付的时候也是调用sdkExecute方法.需要参数有订单编号.订单描述,订单金额,另外一个值是固定值.
/**
* 支付宝支付
* @param shopName 订单描述
* @param unionSn 订单编号
* @param payPrice 支付金额
* @param notifyUrl 回调地址,退款回调地址也用这个 不要求必须https
* @return
*/
public AlipayTradeAppPayResponse alipay(String shopName, String unionSn, double payPrice, String notifyUrl) {
try {
AlipayClient alipayClient = getAlipayClient();
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl(notifyUrl);
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", unionSn);//订单编号
bizContent.put("total_amount", payPrice);//支付金额
// bizContent.put("timeout_express", "30m");
bizContent.put("subject", shopName);//订单描述
bizContent.put("product_code", "QUICK_MSECURITY_PAY");//固定值
request.setBizContent(bizContent.toString());
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
return response;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.退款
回调使用的是订单支付的回调地址.可使用返回值out_biz_no判断.如果不为空就是退款回调.为空就是支付回调.
/**
* 退款通知走下单时候传的notifyurl,商城定单需要传入小订单号
*
* @param orderId 订单号
* @param transactionId 支付宝流水号
* @param money 退还金额
* @param reason 退款原因
* @return
* @throws AlipayApiException
*/
public AlipayTradeRefundResponse refund(String orderId, String transactionId, float money, String reason) throws AlipayApiException {
AlipayClient alipayClient = getAlipayClient();
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("trade_no", transactionId);//支付宝交易号。和商户订单号 out_trade_no 不能同时为空。
bizContent.put("refund_amount", money);
bizContent.put("refund_reason", reason);
bizContent.put("out_request_no", orderId);//支付大单,退单针对小单,该参数必传.如果退款失败.需要使用这个参数重新发起退款
// 返回参数选项,按需传入
JSONArray queryOptions = new JSONArray();
queryOptions.add("refund_detail_item_list");
bizContent.put("query_options", queryOptions);
request.setBizContent(bizContent.toString());
return alipayClient.certificateExecute(request);
}
4.回调
可使用返回值out_biz_no判断.如果不为空就是退款回调.为空就是支付回调.
回调参数中out_trade_no是创建订单或者退款时传入的自己系统内的订单id,如果是退款,out_biz_no
返回值不为空.退款的时候如果是部分退款成功trade_status返回:TRADE_SUCCESS,全部退款返:TRADE_CLOSED ,项目里面分大小单.大单对应对个小单.大单支付小单退款.收到回调后根据自己业务处理,最终要输出success字符,支付宝收到后不在回调.
@ApiOperation("项目阿里支付,退款回调")
@ResponseBody
String jectpayali(HttpServletRequest request) {
HashMap<String, String> params = new HashMap<>();
//获取支付宝POST过来反馈信息
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);
}
//切记alipayPublicCertPath是应用公钥证书路径,请去open.alipay.com对应应用下载。
//boolean AlipaySignature.rsaCertCheckV1(Map<String, String> params, String publicKeyCertPath, String
// charset,String signType)
String sign_type = params.get("sign_type");
try {
if (AlipaySignature.rsaCertCheckV1(params, ALiPay.alipayPublicCert, "utf-8", sign_type)) {
String out_trade_no = params.get("out_trade_no");
JzlProjectOrder projectOrder = mProjectOrderService.findBy("unionSn", out_trade_no);
String out_biz_no = params.get("out_biz_no");//不为空就是退款,为空就是支付回调
if (projectOrder != null) {
if (StringUtils.isEmpty(out_biz_no)) {//,退款流水号为空,走支付回调
if (projectOrder.getStatus() != null && projectOrder.getStatus() == 1) {
return "success";
//支付成功.并且金额相等
} else if (params.get("trade_status").equals("TRADE_SUCCESS") && new BigDecimal(params.get(
"buyer_pay_amount")).compareTo(projectOrder.getPrice()) == 0) {
projectOrder.setStatus(1);
projectOrder.setTransactionId(params.get("trade_no"));
projectOrder.setTimeEnd(params.get("gmt_payment"));
projectOrder.setPayType(1);
mProjectOrderService.update(projectOrder);
return "success";
}
} else {//走 退款回调
if (projectOrder.getStatus() != null && projectOrder.getStatus() == 3) {//已退款
return "success";
//支付成功.并且金额相等,部分退款状态为TRADE_SUCCESS,全额退款为TRADE_CLOSED
} else if (params.get("trade_status").equals("TRADE_CLOSED") && new BigDecimal(params.get(
"refund_fee")).compareTo(projectOrder.getPrice()) == 0) {
projectOrder.setStatus(3);
projectOrder.setTransactionId(params.get("trade_no"));//支付宝交易号
projectOrder.setTimeEnd(params.get("gmt_refund"));
mProjectOrderService.update(projectOrder);
return "success";
}
}
}
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return "error";
}
5.转账
不用异步回调.可直接拿到结果.根据api返回值.需要判断网关是否调用成功.还有转账的状态是否为SUCCESS
/**
* 转账到用户支付宝账户余额
*
* @param orderId 订单di.
* @param money 金额 单位元 最少0.1,2位小数
* @param aliAccount 收款账号.不是用户id,登录支付宝的账号
* @param realName 真实姓名
* @return
*/
public AlipayFundTransUniTransferResponse transfer(String orderId, String money, String aliAccount,
String realName) {
try {
AlipayClient alipayClient = getAlipayClient();
/** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.fund.trans.uni.transfer(单笔转账接口) **/
AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
/** 设置业务参数,具体接口参数传值以文档说明为准:https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer/ **/
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
/** 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一 **/
model.setOutBizNo(orderId);
/** 转账金额,TRANS_ACCOUNT_NO_PWD产品取值最低0.1 **/
model.setTransAmount(money);
/** 产品码,单笔无密转账到支付宝账户固定为:TRANS_ACCOUNT_NO_PWD **/
model.setProductCode("TRANS_ACCOUNT_NO_PWD");
/** 场景码,单笔无密转账到支付宝账户固定为:DIRECT_TRANSFER **/
model.setBizScene("DIRECT_TRANSFER");
/** 转账业务的标题,用于在支付宝用户的账单里显示 **/
model.setOrderTitle("佣金提现");
Participant participant = new Participant();
/** 参与方的唯一标识,收款支付宝账号或者支付宝吧账号唯一会员ID **/
participant.setIdentity(aliAccount);
/** 参与方的标识类型:ALIPAY_USER_ID 支付宝的会员ID **/
participant.setIdentityType("ALIPAY_LOGON_ID");
/** 参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填 **/
participant.setName(realName);
model.setPayeeInfo(participant);
/** 业务备注 **/
model.setRemark("单笔转账");
request.setBizModel(model);
AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
System.out.println(response.getBody());
return response;
} catch (AlipayApiException e) {
e.printStackTrace();
}
/** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/107 **/
return null;
}
微信
很不喜欢微信的文档.要参考好几个.sdk文档:GitHub - wechatpay-apiv3/wechatpay-apache-httpclient: 微信支付 APIv3 Apache HttpClient装饰器(decorator)
.api说明文档:微信支付-开发者文档.
还有个apiv3文档:简介 - WechatPay-API-v3 这个比较重要不好找,里面有签名,验签,加密解密方法
微信也是一样,需要证书.有证书生成工具.生成后自动下载.下载后证书双击安装.里面有个apiclient_key.pem就是证书的私钥.
先说下要用到的参数.
1:appid;
2:商户id;
3apikey,跟老版本一样支付秘钥,
4:v3apikey,v3支付的标志行参数在第三个参数的下面设置,
5:证书序列号,在第三个参数的上面设置证书.可查看证书序列号.
微信的证书不支持网站下载,只能生成的时候下载,保存不好就只能作废了.
1.自动更新证书的client,大概每1小时更新1次,不用管证书,它会自己下载
privateKeyPath:私钥绝对路径,目录之间用/
private HttpClient getHttpClient() throws UnsupportedEncodingException {
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
// 使用定时更新的签名验证器,不需要传入证书
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mPayConfig.getMchCertSN(), merchantPrivateKey);
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mPayConfig.getMchID(),
privateKeySigner);
ScheduledUpdateCertificatesVerifier verifier =
new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,
mPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mPayConfig.getMchID(), mPayConfig.getMchCertSN(), merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
return builder.build();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
添加请求的header参数.v3的好处就是已json传送数据.更易于封装使用.
private HttpPost getHttpPost(String url, String params) {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com" + url);
RequestConfig requestConfig =
RequestConfig.custom().setSocketTimeout(mPayConfig.getHttpReadTimeoutMs()).setConnectTimeout(mPayConfig.getHttpConnectTimeoutMs()).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(params, "UTF-8");
httpPost.addHeader("Content-Type", "application/json; charset=utf-8");
httpPost.addHeader("Accept", "application/json");
// httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mPayConfig.getMchID());
httpPost.setEntity(postEntity);
System.out.println(httpPost.getURI().getRawPath());
return httpPost;
}
为了方便调用.参考支付宝.吧请求参数和返回值都添加的实体bean.属性值就是文档中说明的请求参数key,响应参数key.此处不提供bean代码
用到的方法
2.签名:
public String sign(byte[] message) {
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException | SignatureException | FileNotFoundException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
3.验签:
这个比较坑.如果用ScheduledUpdateCertificatesVerifier对象直接验签的话会不通过.不知道是不是商户平台证书设置的配置.跟踪代码发现去证书的证书id不一样.导致验签不通过.创建Signature对象传入获取到的证书可以验证通过,ScheduledUpdateCertificatesVerifier对象的验证方法就是Signature实现.
/**
* 验证签名 带证书验证
*
* @param message
* @param sign
* @return
* @throws UnsupportedEncodingException
*/
public boolean verify(String message, String sign) {
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
// 使用定时更新的签名验证器,不需要传入证书
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mPayConfig.getMchCertSN(), merchantPrivateKey);
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mPayConfig.getMchID(),
privateKeySigner);
ScheduledUpdateCertificatesVerifier verifier =
new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,
mPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
X509Certificate certificate = verifier.getLatestCertificate();
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(certificate);
signature.update(message.getBytes(StandardCharsets.UTF_8));
return signature.verify(Base64.getDecoder().decode(sign));
} catch (FileNotFoundException | NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return false;
}
4.回调密文解密.敏感信息加密解密
package com.jianlianlian.api.utils.wxpaysdk.v3;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;
public class WxAesUtil {
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
/**
* @param key v3秘钥
*/
public WxAesUtil(byte[] key) {
if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
}
this.aesKey = key;
}
/**
* 回调报文解密
*
* @param associatedData
* @param nonce
* @param ciphertext
* @return
* @throws GeneralSecurityException
* @throws IOException
*/
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
/**
* 敏感信息加密
*
* @param message
* @param certificate
* @return
* @throws IllegalBlockSizeException
* @throws IOException
*/
public static String rsaEncryptOAEP(String message, X509Certificate certificate)
throws IllegalBlockSizeException, IOException {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
byte[] data = message.getBytes("utf-8");
byte[] cipherdata = cipher.doFinal(data);
return Base64.getEncoder().encodeToString(cipherdata);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
/**
* 敏感信息解密
*
* @param ciphertext
* @param privateKey
* @return
* @throws BadPaddingException
* @throws IOException
*/
public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey)
throws BadPaddingException, IOException {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] data = Base64.getDecoder().decode(ciphertext);
return new String(cipher.doFinal(data), "utf-8");
} catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的私钥", e);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new BadPaddingException("解密失败");
}
}
}
5.支付
需要注意微信支付单位是分.如果是doubel类型最好转string用BigDecimal拿值.
/**
* @param desc 订单描述
* @param Out_trade_no 订单编号
* @param payAmount 支付进度 单位元
* @param notifyUrl 通知url 可为null
* @return
*/
public WxAppPayParams pay(String desc, String Out_trade_no, double payAmount, String notifyUrl) {
try {
WxPayParams params = new WxPayParams();
params.setOut_trade_no(Out_trade_no);
params.setDescription(desc);
if (!StringUtils.isEmpty(notifyUrl)) {
params.setNotify_url(notifyUrl);
}
WxAmount amount = new WxAmount();
amount.setTotal(new BigDecimal(String.valueOf(payAmount)).multiply(new BigDecimal(100)).intValue());
params.setAmount(amount);
params.setAppid(mPayConfig.getAppID());
params.setMchid(mPayConfig.getMchID());
String payParams = JSON.toJSONString(params);
System.out.println(payParams);
HttpResponse httpResponse =
getHttpClient().execute(getHttpPost(WXPayConstants.V3_PLACE_ORDER, payParams));
String text = EntityUtils.toString(httpResponse.getEntity());
System.out.println(text);
WXPayResult wxPayResult = JSON.parseObject(text, WXPayResult.class);
if (wxPayResult != null) {
WxAppPayParams wxAppPayParams = new WxAppPayParams();
wxAppPayParams.setAppid(mPayConfig.getAppID());
wxAppPayParams.setPartnerid(mPayConfig.getMchID());
wxAppPayParams.setPrepayid(wxPayResult.getPrepay_id());
StringBuffer sb = new StringBuffer();
sb.append(wxAppPayParams.getAppid()).append("\n").append(wxAppPayParams.getTimestamp()).append("\n").append(wxAppPayParams.getNoncestr()).append("\n").append(wxPayResult.getPrepay_id()).append("\n");
wxAppPayParams.setSign(sign(sb.toString().getBytes("UTF-8")));
return wxAppPayParams;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
6.回调
支付和退款回调可分开设置,分别传入notifyurl,也可以一样自己处理.先验签以保证接口是微信调用.项目里面分大小单.大单对应对个小单.大单支付小单退款,unionSn(大单号),orderSn(小单号)
@ApiOperation("项目微信支付回调")
@ResponseBody
String jectpay(HttpServletRequest request) {
String resultSign = request.getHeader("Wechatpay-Signature"); //回调验签
String resultNonce = request.getHeader("Wechatpay-Nonce");//回调随机串
String resultTimestamp = request.getHeader("Wechatpay-Timestamp");//回调时间搓
try {
ServletInputStream inputStream = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer resultBuffer = new StringBuffer();
String line;
while ((line = bufferedReader.readLine()) != null) {
resultBuffer.append(line);
}
inputStream.close();
bufferedReader.close();
String resultBody = resultBuffer.toString();
String originStr = Stream.of(resultTimestamp, resultNonce, resultBody).collect(Collectors.joining("\n",
"", "\n"));
if (mWxV3Pay.verify(originStr, resultSign)) {
WxNotify notify = JSON.parseObject(resultBody, WxNotify.class);
WxAesUtil aesUtil = new WxAesUtil(mWxV3Pay.getPayConfig().getV3Key().getBytes(StandardCharsets.UTF_8));
String decrypt = aesUtil.decryptToString(notify.getResource().getAssociated_data().getBytes(),
notify.getResource().getNonce().getBytes(), notify.getResource().getCiphertext());
WXNotifyDecode notifyDecode = JSON.parseObject(decrypt, WXNotifyDecode.class);
String out_trade_no = notifyDecode.getOut_trade_no();
JzlProjectOrder projectOrder = mProjectOrderService.findBy("unionSn", out_trade_no);
if (projectOrder != null) {
if (projectOrder.getStatus() != null && projectOrder.getStatus() == 1) {
JSONObject object = new JSONObject();
object.put("code", "SUCCESS");
object.put("message", "成功");
return object.toJSONString();
//支付成功.并且金额相等
} else if (notifyDecode.getTrade_state().equals("SUCCESS") && new BigDecimal(notifyDecode.getAmount().getTotal()).compareTo(projectOrder.getPrice().multiply(new BigDecimal(100))) == 0) {
projectOrder.setStatus(1);
projectOrder.setTransactionId(notifyDecode.getTransaction_id());
String success_time =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new SimpleDateFormat("yyyy-MM" +
"-ddTHH:mm:ss").parse(notifyDecode.getSuccess_time()));
projectOrder.setTimeEnd(success_time);
mProjectOrderService.update(projectOrder);
JSONObject object = new JSONObject();
object.put("code", "SUCCESS");
object.put("message", "成功");
return object.toJSONString();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
JSONObject object = new JSONObject();
object.put("code", "error");
object.put("message", "失败");
return object.toJSONString();
}
退款
需要传入支付流水号.退款单号.订单总金额和退款金额.还有一个币种默认CNY.参考api参入必传参数,退款回调需要验证Refund_status是否为SUCCESS,处理完之后返回一个json传:
{
"code": "SUCCESS",
"message": "成功"
}
/**
* 退款,商城订单需要传入小订单号
*
* @param refundParams
* @return
*/
public WXRefundResult refund(WXRefundParams refundParams) {
try {
HttpPost httpPost = getHttpPost(WXPayConstants.V3_REFUND, JSON.toJSONString(refundParams));
// 后面跟使用Apache HttpClient一样
HttpResponse httpResponse = getHttpClient().execute(httpPost);
String text = EntityUtils.toString(httpResponse.getEntity());
System.out.println(text);
return JSON.parseObject(text, WXRefundResult.class);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
转账
微信转账要求开通微信的90天之后的前30天每天都有正常的交易流水.暂时不满足条件.