使用java调用支付宝进行提现

1、引入依赖 pom.xml

        <!-- 支付宝支付 --><!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.10.81.ALL</version></dependency><!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.66</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version></dependency>

2、创建接收参数对象  WithdrawApplyDto.java

package com.model.bo;import lombok.Data;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;/*** 提现申请** @author kou*/
@Data
public class WithdrawApplyDto {/*** 金额,微信单位:分 ,支付宝单位:元*/@NotBlank(message = "amount金额不能为空")private String amount;}

创建微信提现对象

package com.model.bo;import lombok.Data;
import org.hibernate.validator.constraints.Range;import javax.validation.constraints.NotNull;/*** 微信支付** @author kou*/
@Data
public class WechatPay {/*** 商户订单号, 只能是字母或者数字*/private String tradeNo;/*** 随机字符串*/private String nonceStr;/*** 微信id*/private String wxOpenId;/*** 收款用户姓名* 如果check_name设置为FORCE_CHECK,则必填用户真实姓名* 如需电子回单,需要传入收款用户姓名*/private String reUserName;/*** 校验用户姓名选项* NO_CHECK:不校验真实姓名* FORCE_CHECK:强校验真实姓名*/private String checkName;/*** 金额,单位分*/@Range(min = 600, max = 6000, message = "提现金额最低6.00元或累计超过60.00元")@NotNull(message = "amount金额不能为空")private Integer amount;/*** 订单说明*/private String desc;/*** 该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。*/private String ip;/*** 微信支付分配的终端设备号*/private String deviceInfo;}

3、创建controller

package com.controller;import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.alipay.api.domain.Participant;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.constants.BaseConstants;
import com.model.entity.BaseUser;
import com.model.ResultBody;
import com.constatns.WalletConstants;
import com.model.bo.WechatPay;
import com.model.bo.WithdrawApplyDto;
import com.service.IWalletService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;/*** 个人钱包控制器** @author kou*/
@Slf4j
@Validated
@Api(value = "个人钱包", tags = "个人钱包")
@RequestMapping("/personal")
@RestController
public class WalletController {@Autowiredprivate IWalletService walletService;/*** 提现支付宝** @param withdrawApplyDto 提现信息* @return 结果*/@ApiOperation(value = "提现支付宝", notes = "提现支付宝")@PostMapping("/withdraw/alipay")public ResultBody aliPayWithdraw(@Validated @RequestBody WithdrawApplyDto withdrawApplyDto) {// 获取用户信息,根据实际使用BaseUser user = new BaseUser();log.info("用户申请提现金额 {} 到支付宝", withdrawApplyDto.getAmount());// 获取openidString openId = "";// 提现信息AlipayFundTransPayModel model = new AlipayFundTransPayModel();// 转账标题model.setOrderTitle("提现");// 提现金额model.setTransAmount(withdrawApplyDto.getAmount());// 提现备注model.setRemark("用户提现");model.setProductCode("TRANS_ACCOUNT_NO_PWD");model.setBizScene("DIRECT_TRANSFER");//收款方信息Participant participant = new Participant();//参与方的唯一标识participant.setIdentity(openId);//参与方的标识类型,目前支持如下类型://1、ALIPAY_USER_ID 支付宝的会员ID//2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式participant.setIdentityType("ALIPAY_USER_ID");// 参与方真实姓名,如果非空,将校验收款支付宝账号姓名一致性。当identity_type=ALIPAY_LOGON_ID时,本字段必填。// participant.setName("寇");model.setPayeeInfo(participant);try {ResultBody result = walletService.alipayWithdraw(user.getUserId(), withdrawApplyDto, model);return result;} catch (JsonProcessingException e) {log.error(e.getMessage(), e);} catch (AlipayApiException e) {log.error(e.getMessage(), e);}return ResultBody.failed().msg("提现失败");}/*** 提现微信** @param withdrawApplyDto 提现信息* @return 结果*/@ApiOperation(value = "提现微信", notes = "提现微信")@PostMapping("/withdraw/wechat")public ResultBody wechatWithdraw(@Validated @RequestBody WithdrawApplyDto withdrawApplyDto) {// 获取用户,根据实际情况获取BaseUser user = new BaseUser();log.info("用户 {} 申请提现金额 {} 到微信", user.getUserId(), withdrawApplyDto.getAmount());// 获取openidString openId = "";WechatPay wechatPay = new WechatPay();// 将元变为分BigDecimal fen = new BigDecimal(withdrawApplyDto.getAmount()).multiply(new BigDecimal(100));wechatPay.setAmount(fen.intValue());wechatPay.setWxOpenId(openId);// 不校验名字wechatPay.setCheckName("NO_CHECK");wechatPay.setDesc("提现");try {ResultBody result = walletService.wechatWithdraw(user.getUserId(), wechatPay);return result;} catch (JsonProcessingException e) {log.error(e.getMessage(), e);} catch (Exception e) {log.error(e.getMessage(), e);}return ResultBody.failed().msg("提现失败");}}

4、service代码

package com.service;import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.model.ResultBody;
import com.model.bo.WechatPay;
import com.model.bo.WithdrawApplyDto;import java.math.BigDecimal;
import java.util.List;/*** 钱包业务接口** @author kou*/
public interface IWalletService {/*** 微信提现** @param userId    用户id* @param wechatPay 提现信息* @return 结果*/ResultBody wechatWithdraw(Long userId, WechatPay wechatPay) throws JsonProcessingException;/*** 支付宝提现** @param userId           用户id* @param withdrawApplyDto 提现申请信息* @param model            提现信息* @return 结果*/ResultBody alipayWithdraw(Long userId, WithdrawApplyDto withdrawApplyDto, AlipayFundTransPayModel model) throws JsonProcessingException, AlipayApiException;}

实现类

package com.service.impl;import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.model.ResultBody;
import com.constatns.WalletConstants;
import com.model.bo.WechatPay;
import com.model.bo.WithdrawApplyDto;
import com.service.IWalletService;
import com.service.WithdrawApplyService;
import com.service.ZyCashbankService;
import com.util.AlipayTransferUtil;
import com.util.WeiXinTransfersUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Map;
import java.util.UUID;/*** 钱包业务接口实现类** @author kou*/
@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class WalletServiceImpl implements IWalletService {@Autowiredprivate ZyCashbankService cashbankService;@Autowiredprivate AlipayTransferUtil alipayTransferUtil;@Autowiredprivate WeiXinTransfersUtil weiXinTransfersUtil;@Autowiredprivate WithdrawApplyService withdrawApplyService;@Autowiredprivate RedissonClient redissonClient;/*** 提现分布式锁key*/private static String withdrawLockKey = "wallet:withdraw:apply:";/*** 微信提现** @param userId    用户id* @param wechatPay 提现信息*/@GlobalTransactional@Overridepublic ResultBody wechatWithdraw(Long userId, WechatPay wechatPay) throws JsonProcessingException {// 获取锁RLock redissonLock = redissonClient.getLock(withdrawLockKey + userId);try {// 加锁log.info("开始获取微信提现锁...");redissonLock.lock();log.info("开始减少用户提现毛栗数");// 减少用户现金银行毛栗数boolean cashResult = cashbankService.subtractNumber(userId, wechatPay.getAmount(), WalletConstants.CASH_USE_TYPE_WITHDRAW, "用户提现[微信]");if (!cashResult) {// 扣除企业毛栗失败log.error("减少用户现金银行毛栗数失败,用户id:{},毛栗数:{}", userId, wechatPay.getAmount());throw new RuntimeException("减少用户现金银行毛栗数失败");}// 生成订单编号String orderNo = withdrawApplyService.generateOutTradeNo();log.info("订单编号:{}", orderNo);// 生成订单编号wechatPay.setTradeNo(orderNo);wechatPay.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));log.info("----- 开始微信转账 -----");Map<String, String> responseData = weiXinTransfersUtil.transfer(wechatPay);if ("SUCCESS".equals(responseData.get("return_code")) && "SUCCESS".equals(responseData.get("result_code"))) {log.info("----- 开始微信转账成功 -----");log.info("转账信息:{}", responseData);// 保存记录} else {ObjectMapper mapper = new ObjectMapper();log.info("转账失败,失败原因:{}", mapper.writeValueAsString(responseData));String reason = "";// 获取微信返回错误if (StringUtils.isNotBlank(responseData.get("return_code"))) {reason = responseData.get("return_code");}throw new RuntimeException("转账失败:" + reason);}} finally {// 释放锁redissonLock.unlock();log.info("开始释放微信提现锁...");}}/*** 支付宝提现** @param userId           用户id* @param withdrawApplyDto 提现申请信息* @param model            提现信息* @return 提现结果*/@GlobalTransactional@Overridepublic ResultBody alipayWithdraw(Long userId, WithdrawApplyDto withdrawApplyDto, AlipayFundTransPayModel model) throws JsonProcessingException, AlipayApiException {// 获取锁RLock redissonLock = redissonClient.getLock(withdrawLockKey + userId);try {// 加锁log.info("开始获取支付宝提现锁...");redissonLock.lock();log.info("开始减少用户提现毛栗数");// 减少用户现金银行毛栗数boolean cashResult = cashbankService.subtractNumber(userId, withdrawApplyDto.getAmount(), WalletConstants.CASH_USE_TYPE_WITHDRAW, "用户提现[支付宝]");if (!cashResult) {// 扣除企业毛栗失败log.error("减少用户现金银行毛栗数失败,用户id:{},毛栗数:{}", userId, withdrawApplyDto.getAmount());throw new RuntimeException("减少用户现金银行毛栗数失败");}// 订单编号为空则重新生成if (StringUtils.isBlank(model.getOutBizNo())) {// 生成订单编号model.setOutBizNo(withdrawApplyService.generateOutTradeNo());}log.info("订单编号:{}", model.getOutBizNo());log.info("----- 开始支付宝转账 -----");AlipayFundTransUniTransferResponse response = alipayTransferUtil.transferCert(model);ObjectMapper mapper = new ObjectMapper();if (response.isSuccess()) {log.info("支付宝转账成功, 转账信息 {}", mapper.writeValueAsString(response));// 保存记录} else {log.info("支付宝转账失败,失败原因:{}", mapper.writeValueAsString(response));throw new RuntimeException("支付宝转账失败:" + response.getSubMsg());}} finally {// 释放锁redissonLock.unlock();log.info("开始释放支付宝提现锁...");}}}

5、支付宝配置

bootstrap.yml

# 支付配置
pay:config:# 最小限制 100 毛栗minLimit: 10# 最小提现金额 10 元minAmountLimit: 1# 最大额度5000 毛栗maxLimit: 5000# 最大提现金额 500 元maxAmountLimit: 500# 最大提现次数限制maxTimes: 3# 提现整倍数限制 (毛栗数)multiple: 10# 提现税率,提现后金额 = 提现金额 * 税率rate: 0.4# 支付宝配置alipay:app-id: ********private-key: *********public-key: **********sign-type: RSA2format: jsoncharset: UTF-8gateway-url: https://openapi.alipay.com/gateway.do# 应用公钥证书路径app-cert-path: crt/alipay/appCertPublicKey.crt# 支付宝公钥证书文件路径alipay-cert-path: crt/alipay/alipayCertPublicKey_RSA2.crt# 支付宝根证书文件路径alipay-root-cert-path: crt/alipay/alipayRootCert.crt# 回调地址return-url: /# 异步地址notify-url: /wallet/pay/notify/alipay# 收款码超时时效 10分钟qr-code-timeout-express: 10m# 微信配置wxpay:appId: *****mchId: *******appKey: *******certPath: crt/wxpay/apiclient_cert.p12keyPath: crt/wxpay/apiclient_key.pem# 最小额度6元,单位为分minLimit: 600# 最大额度6元,单位为分maxLimit: 6000
package com.server.util;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 支付配置** @author kou*/
@Data
@ConfigurationProperties(prefix = "pay.config")
@Component
public class PayConfig {/*** 最小提现额度*/private Integer minLimit;/*** 最小提现金额*/private String minAmountLimit;/*** 最大提现额度*/private Integer maxLimit;/*** 最大提现金额*/private String maxAmountLimit;/*** 最大提现次数限制*/private Integer maxTimes;/*** 提现整倍数限制*/private Integer multiple;/*** 提现税率,提现后金额 = 提现金额 * 税率*/private String rate;}
package com.server.util;import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** 支付宝配置** @author kou*/
@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.alipay")
@Component
public class AliPayProperties {/*** 支付宝网关*/private String gatewayUrl;/*** 支付宝 app-id*/private String appId;/*** 支付宝 私钥*/private String privateKey;/*** 支付宝公钥*/private String publickey;/*** 支付宝 签名类型*/private String signType;/*** 格式*/private String format;/*** 格式*/private String charset;/*** 应用公钥证书路径*/private String appCertPath;/*** 支付宝公钥证书文件路径*/private String alipayCertPath;/*** 支付宝CA根证书文件路径*/private String alipayRootCertPath;/*** 收款码时效时间*/private String qrCodeTimeoutExpress;/*** 回调地址*/private String returnUrl;/*** 异步地址*/private String notifyUrl;/*** 最大查询次数*/private static int maxQueryRetry = 5;/*** 查询间隔(毫秒)*/private static long queryDuration = 5000;/*** 最大撤销次数*/private static int maxCancelRetry = 3;/*** 撤销间隔(毫秒)*/private static long cancelDuration = 3000;/*** PostContruct是spring框架的注解,在spring容器初始化的时候执行该方法。*/// @PostConstructpublic void init() {log.info(description());}public String description() {StringBuilder sb = new StringBuilder("\n支付宝Configs\n{\n");sb.append("支付宝网关: ").append(gatewayUrl).append("\n");sb.append("appId: ").append(appId).append("\n");sb.append("商户RSA私钥: ").append("***").append("\n");sb.append("应用公钥证书: ").append(appCertPath).append("\n");sb.append("支付宝公钥证书: ").append(alipayCertPath).append("\n");sb.append("支付宝根证书: ").append(alipayRootCertPath).append("\n");sb.append("支付宝RSA公钥: ").append("***").append("\n");sb.append("签名类型: ").append(signType).append("\n");sb.append("查询重试次数: ").append(maxQueryRetry).append("\n");sb.append("查询间隔(毫秒): ").append(queryDuration).append("\n");sb.append("撤销尝试次数: ").append(maxCancelRetry).append("\n");sb.append("撤销重试间隔(毫秒): ").append(cancelDuration).append("\n");sb.append("}");return sb.toString();}}
package com.server.util;import com.alipay.api.AlipayApiException;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;import java.io.FileNotFoundException;
import java.io.IOException;/*** 支付配置** @author kou*/
@Data
@Slf4j
@Configuration
@EnableConfigurationProperties(AliPayProperties.class)
public class AliPayConfig {@Autowiredprivate AliPayProperties aliPayProperties;/*** 获取证书根路径** @return 路径* @throws FileNotFoundException*/public String certRootPath() throws FileNotFoundException {//判断系统环境String osName = System.getProperty("os.name");log.info("-------系统环境 {} --------", osName);String filePath = null;if (osName.startsWith("Windows")) {// windowsfilePath = ResourceUtils.getURL("classpath:").getPath();} else {// unix or linuxfilePath = System.getProperty("user.dir") + "/";}log.info("-------------文件路径 {} ----------", filePath);return filePath;}/*** 支付宝接口加签模式为公钥证书时使用* https://blog.csdn.net/qq_37272886/article/details/107480123** @return*/@Beanpublic DefaultAlipayClient alipayClient() throws AlipayApiException, IOException {String filePath = certRootPath();CertAlipayRequest certAlipayRequest = new CertAlipayRequest();// 设置网关地址certAlipayRequest.setServerUrl(aliPayProperties.getGatewayUrl());// 设置应用IdcertAlipayRequest.setAppId(aliPayProperties.getAppId());// 设置应用私钥certAlipayRequest.setPrivateKey(aliPayProperties.getPrivateKey());// 设置请求格式,固定值jsoncertAlipayRequest.setFormat(aliPayProperties.getFormat());// 设置字符集certAlipayRequest.setCharset(aliPayProperties.getCharset());// 设置签名类型certAlipayRequest.setSignType(aliPayProperties.getSignType());// 设置应用公钥证书路径certAlipayRequest.setCertPath(filePath + aliPayProperties.getAppCertPath());// 设置支付宝公钥证书路径certAlipayRequest.setAlipayPublicCertPath(filePath + aliPayProperties.getAlipayCertPath());// 设置支付宝根证书路径certAlipayRequest.setRootCertPath(filePath + aliPayProperties.getAlipayRootCertPath());return new DefaultAlipayClient(certAlipayRequest);}}
package com.server.util;import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.model.bo.ali.AliPayTransfer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;/*** 阿里转账* document: https://opendocs.alipay.com/apis/api_28/alipay.fund.trans.uni.transfer** @author kou*/
@Slf4j
@Component
public class AlipayTransferUtil {@Autowiredprivate AliPayProperties aliPayProperties;@Autowiredprivate AlipayClient alipayClient;@Autowiredprivate AliPayConfig aliPayConfig;/*** 支付配置*/@Autowiredprivate PayConfig payConfig;/*** 项目域名*/@Value("${opencloud.common.api-server-addr}")private String apiServer;/*** 校验** @param params 校验参数* @return 校验结果*/public boolean validateCert(Map<String, String> params) throws AlipayApiException, FileNotFoundException {log.info("开始校验参数信息");String alipayCertPath = aliPayConfig.certRootPath() + aliPayProperties.getAlipayCertPath();boolean result = AlipaySignature.rsaCertCheckV1(params,alipayCertPath,aliPayProperties.getCharset(),aliPayProperties.getSignType());if (result) {log.info("参数合法");} else {log.error("校验参数合法性失败");}return result;}/*** 转账** @param transfer 转账信息*/@Deprecatedpublic AlipayFundTransUniTransferResponse transfer(AliPayTransfer transfer) throws AlipayApiException, JsonProcessingException {ObjectMapper mapper = new ObjectMapper();// 处理金额乘以税率transfer.setTransAmount(transfer.getTransAmount().multiply(BigDecimal.ONE.subtract(new BigDecimal(payConfig.getRate()))));String params = mapper.writeValueAsString(transfer);log.info("转账参数:{}", params);AlipayClient alipayClient = new DefaultAlipayClient(aliPayProperties.getGatewayUrl(), aliPayProperties.getAppId(),aliPayProperties.getPrivateKey(), aliPayProperties.getFormat(), aliPayProperties.getCharset(), aliPayProperties.getPublickey(), aliPayProperties.getSignType());AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();request.setBizContent(params);AlipayFundTransUniTransferResponse response = alipayClient.execute(request);if (response.isSuccess()) {log.info("转账成功...");} else {log.info("转账失败,失败原因:{}");}return response;}/*** 使用证书转账* https://opendocs.alipay.com/open/309/106236#%E4%B8%8B%E8%BD%BD%E6%9C%8D%E5%8A%A1%E7%AB%AF%20SDK** @param transfer 转账信息*/@Deprecatedpublic AlipayFundTransUniTransferResponse transferCert(AliPayTransfer transfer) throws AlipayApiException, JsonProcessingException, FileNotFoundException {ObjectMapper mapper = new ObjectMapper();// 处理金额乘以税率transfer.setTransAmount(transfer.getTransAmount().multiply(BigDecimal.ONE.subtract(new BigDecimal(payConfig.getRate()))));String params = mapper.writeValueAsString(transfer);log.info("转账参数:{}", params);String filePath = ResourceUtils.getURL("classpath:").getPath();// 构造clientCertAlipayRequest certAlipayRequest = new CertAlipayRequest();// 设置网关地址certAlipayRequest.setServerUrl(aliPayProperties.getGatewayUrl());// 设置应用IdcertAlipayRequest.setAppId(aliPayProperties.getAppId());// 设置应用私钥certAlipayRequest.setPrivateKey(aliPayProperties.getPrivateKey());// 设置请求格式,固定值jsoncertAlipayRequest.setFormat(aliPayProperties.getFormat());// 设置字符集certAlipayRequest.setCharset(aliPayProperties.getCharset());// 设置签名类型certAlipayRequest.setSignType(aliPayProperties.getSignType());// 设置应用公钥证书路径certAlipayRequest.setCertPath(filePath + aliPayProperties.getAppCertPath());// 设置支付宝公钥证书路径certAlipayRequest.setAlipayPublicCertPath(filePath + aliPayProperties.getAlipayCertPath());// 设置支付宝根证书路径certAlipayRequest.setRootCertPath(filePath + aliPayProperties.getAlipayRootCertPath());// 构造clientAlipayClient alipayClient2 = new DefaultAlipayClient(certAlipayRequest);// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.payAlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();request.setBizContent(params);AlipayFundTransUniTransferResponse response = alipayClient2.certificateExecute(request);if (response.isSuccess()) {log.info("转账成功, 转账信息 {}", mapper.writeValueAsString(response));} else {log.info("转账失败,失败原因:{}", mapper.writeValueAsString(response));}return response;}/*** 使用证书转账** @param model 转账信息*/public AlipayFundTransUniTransferResponse transferCert(AlipayFundTransPayModel model) throws AlipayApiException, JsonProcessingException {if (null == model) {return null;}log.info("------------支付宝开始转账--------------");ObjectMapper mapper = new ObjectMapper();log.info("转账税前参数:{}", mapper.writeValueAsString(model));// 处理金额乘以税率BigDecimal amount = new BigDecimal(model.getTransAmount()).multiply(BigDecimal.ONE.subtract(new BigDecimal(payConfig.getRate())));// 设置税率后金额model.setTransAmount(amount.setScale(2, BigDecimal.ROUND_DOWN).toString());log.info("转账参数:{}", mapper.writeValueAsString(model));log.info("开始转账");// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.payAlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();request.setBizModel(model);AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);if (response.isSuccess()) {log.info("转账成功, 转账信息 {}", mapper.writeValueAsString(response));} else {log.info("转账失败,失败原因:{}", mapper.writeValueAsString(response));}return response;}/*** 统一收单线下交易预创建* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate?scene=API002020081000013487** @param outTradeNo  商户订单号,需要保证商家系统不重复* @param totalAmount 订单金额* @param subject     商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等* @return 生成结果* @throws AlipayApiException* @throws JsonProcessingException*/public AlipayTradePrecreateResponse precreate(String outTradeNo,String totalAmount,String subject) throws AlipayApiException, JsonProcessingException {return this.precreate(outTradeNo, totalAmount, subject, aliPayProperties.getQrCodeTimeoutExpress());}/*** 统一收单线下交易预创建* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate?scene=API002020081000013487** @param outTradeNo     商户订单号,需要保证商家系统不重复* @param totalAmount    订单金额* @param subject        商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等* @param timeoutExpress 该笔订单允许的最晚付款时间,逾期将关闭交易,从生成二维码开始计时。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)*                       该参数数值不接受小数点, 如 1.5h,可转换为 90m* @return 生成结果* @throws AlipayApiException* @throws JsonProcessingException*/public AlipayTradePrecreateResponse precreate(String outTradeNo,String totalAmount,String subject,String timeoutExpress) throws AlipayApiException, JsonProcessingException {return this.precreate(outTradeNo, totalAmount, subject, null, timeoutExpress);}/*** 统一收单线下交易预创建* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate?scene=API002020081000013487** @param outTradeNo     商户订单号,需要保证商家系统不重复* @param totalAmount    订单金额* @param subject        商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等* @param goodsDetail    商品明细* @param timeoutExpress 该笔订单允许的最晚付款时间,逾期将关闭交易,从生成二维码开始计时。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)*                       该参数数值不接受小数点, 如 1.5h,可转换为 90m* @return 生成结果* @throws AlipayApiException* @throws JsonProcessingException*/public AlipayTradePrecreateResponse precreate(String outTradeNo,String totalAmount,String subject,List<GoodsDetail> goodsDetail,String timeoutExpress) throws AlipayApiException, JsonProcessingException {AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();// 设置商品编号model.setOutTradeNo(outTradeNo);// 设置商品总额model.setTotalAmount(totalAmount);model.setSubject(subject);// 设置商品明细model.setGoodsDetail(goodsDetail);// 设置商品时效model.setQrCodeTimeoutExpress(timeoutExpress);return this.precreate(model);}/*** 统一收单线下交易预创建* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.precreate?scene=API002020081000013487** @param model 生成收款二维码* @return 生成结果* @throws AlipayApiException* @throws JsonProcessingException*/public AlipayTradePrecreateResponse precreate(AlipayTradePrecreateModel model) throws AlipayApiException, JsonProcessingException {log.info("------------支付宝开始生成收款二维码--------------");// 未设置二维码时效则默认时效if (StringUtils.isBlank(model.getQrCodeTimeoutExpress())) {// 默认设置10分钟model.setQrCodeTimeoutExpress(aliPayProperties.getQrCodeTimeoutExpress());}ObjectMapper mapper = new ObjectMapper();log.info("生成二收款维码参数:{}", mapper.writeValueAsString(model));AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();request.setBizModel(model);// 设置异步回调地址log.info("异步回调地址:{}", apiServer + aliPayProperties.getNotifyUrl());request.setNotifyUrl(apiServer + aliPayProperties.getNotifyUrl());AlipayTradePrecreateResponse response = alipayClient.certificateExecute(request);if (response.isSuccess()) {log.info("生成成功, 结果 {}", mapper.writeValueAsString(response));} else {log.info("生成失败, 结果 {}", mapper.writeValueAsString(response));}return response;}/*** 统一收单线下交易查询* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.query** @param tradeNo    支付宝交易号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 生成结果* @throws AlipayApiException*/public AlipayTradeQueryResponse query(String tradeNo, String outTradeNo) throws AlipayApiException {AlipayTradeQueryModel model = new AlipayTradeQueryModel();model.setTradeNo(tradeNo);model.setOutTradeNo(outTradeNo);return this.query(model);}/*** 统一收单线下交易查询* document: https://opendocs.alipay.com/apis/api_1/alipay.trade.query** @param model 订单信息* @return 生成结果* @throws AlipayApiException*/public AlipayTradeQueryResponse query(AlipayTradeQueryModel model) throws AlipayApiException {log.info("------------支付宝查询订单--------------");AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();request.setBizModel(model);AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);if (response.isSuccess()) {log.info("查询成功!");} else {log.info("查询失败, 原因,code:{},msg:{}", response.getSubCode(), response.getSubMsg());}return response;}/*** 同一订单多次退款** @param orderNo      商户订单号* @param refundAmount 退款金额* @param refundReason 退款院原因* @return*/public AlipayTradeRefundResponse multipleRefunds(String orderNo, String refundAmount, String refundReason) throws AlipayApiException {log.info("------------支付宝退款开始--------------");AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();AlipayTradeRefundModel model = new AlipayTradeRefundModel();// 商户订单号model.setOutTradeNo(orderNo);// 退款金额 单位元model.setRefundAmount(refundAmount);// 退款原因model.setRefundReason(refundReason);// 退款订单号(同一个订单可以分多次部分退款,当分多次时必传)model.setOutRequestNo(UUID.randomUUID().toString());log.info("------退款参数-----{}-----{}-----{}------{}", orderNo, refundAmount, refundReason, model.getOutRequestNo());alipayRequest.setBizModel(model);AlipayTradeRefundResponse response = alipayClient.certificateExecute(alipayRequest);if (response.isSuccess()) {log.info("查询成功!");} else {log.info("查询失败, 原因,code:{},msg:{}", response.getSubCode(), response.getSubMsg());}return response;}}
package com.server.util;import javax.servlet.http.HttpServletRequest;/*** 帮助类** @author kou*/
public class CommonUtil {/*** 获取 ip 地址** @param request* @return*/public static String getIpAddr(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}if (ip.indexOf(",") != -1) {String[] ips = ip.split(",");if (ips.length > 1) {ip = ips[0];}}return ip;}
}
package com.util.alipay;/*** 支付宝支付常量** @author kou*/
public class AliPayConstants {/*** 支付宝支付状态成功*/public static final String TRADE_STATUS_SUCCESS = "TRADE_SUCCESS";}

6、微信工具类

package com.util.wxpay;import org.apache.http.client.HttpClient;/*** 微信支付常量** @author kou*/
public class WXPayConstants {public enum SignType {MD5, HMACSHA256}public static final String DOMAIN_API = "https://api.mch.weixin.qq.com";public static final String DOMAIN_API2 = "https://api2.mch.weixin.qq.com";public static final String DOMAIN_APIHK = "https://apihk.mch.weixin.qq.com";public static final String DOMAIN_APIUS = "https://apius.mch.weixin.qq.com";/*** 交易类型 JSAPI支付*/public static final String TRADE_TYPE_JSAPI = "JSAPI";/*** 交易类型 Native支付*/public static final String TRADE_TYPE_NATIVE = "NATIVE";/*** 交易类型 APP支付*/public static final String TRADE_TYPE_APP = "APP";public static final String FAIL = "FAIL";public static final String SUCCESS = "SUCCESS";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 WXPAYSDK_VERSION = "WXPaySDK/3.0.9";public static final String USER_AGENT = WXPAYSDK_VERSION +" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();/*** 企业支付*/public static final String TRANSFERS_URL_SUFFIX = "/mmpaymkttransfers/promotion/transfers";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";/*** 微信native支付回调地址*/public static final String NOTIFY_URL = "/wallet/pay/notify/wxpay";}
package com.util.wxpay;import org.w3c.dom.Document;import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;/*** 2018/7/3*/
public final class WXPayXmlUtil {public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);documentBuilderFactory.setXIncludeAware(false);documentBuilderFactory.setExpandEntityReferences(false);return documentBuilderFactory.newDocumentBuilder();}public static Document newDocument() throws ParserConfigurationException {return newDocumentBuilder().newDocument();}
}
package com.server.util;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 微信支付配置** @author kou*/
@Data
@ConfigurationProperties(prefix = "pay.wxpay")
@Component
public class WeChatPayConfig {/*** 应用id,appId*/private String appId;/*** 商户id*/private String mchId;/*** 秘钥*/private String appKey;/*** 证书路径*/private String certPath;/*** key路径*/private String keyPath;/*** 最小提现额度*/private Integer minLimit;/*** 最大提现额度*/private Integer maxLimit;}
package com.server.util;import com.model.bo.WechatPay;
import com.util.wxpay.WXPayConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.net.ssl.SSLContext;
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.*;
import java.math.BigDecimal;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;/*** 微信支付*/
@Slf4j
@Component
public class WeiXinTransfersUtil {@Autowiredprivate WeChatPayConfig weChatPayConfig;@Autowiredprivate PayConfig payConfig;/*** 项目域名*/@Value("${opencloud.common.api-server-addr}")private String apiServer;/*** 转账* document:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2#** @param wechatPay 转账信息*/public Map<String, String> transfer(WechatPay wechatPay) {SortedMap<String, String> map = new TreeMap<>();// 设置公共参数setCommonParams(map);// 随机字符串map.put("nonce_str", wechatPay.getNonceStr());// openidmap.put("openid", wechatPay.getWxOpenId());// 本地订单编号map.put("partner_trade_no", wechatPay.getTradeNo());// 金额BigDecimal amount = BigDecimal.ONE.subtract(new BigDecimal(payConfig.getRate())).multiply(new BigDecimal(wechatPay.getAmount()));// map.put("amount", String.valueOf(wechatPay.getAmount()));map.put("amount", amount.intValue() + "");// 校验用户姓名选项 NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名map.put("check_name", "NO_CHECK");// 收款人真实姓名if (StringUtils.isNotBlank(wechatPay.getReUserName())) {map.put("re_user_name", wechatPay.getReUserName());}// 企业付款备注if (StringUtils.isNotBlank(wechatPay.getDesc())) {map.put("desc", wechatPay.getDesc());}// Ip地址if (StringUtils.isNotBlank(wechatPay.getIp())) {map.put("spbill_create_ip", wechatPay.getIp());}// 设备号if (StringUtils.isNotBlank(wechatPay.getDeviceInfo())) {map.put("device_info", wechatPay.getDeviceInfo());}// 设置签名map.put("sign", gennerateSign(map, weChatPayConfig.getAppKey()));// 讲参数转换为微信支付xmlString orderInfo = mapToXml(map);String response = null;try {response = httpRequest(WXPayConstants.TRANSFERS_URL_SUFFIX, orderInfo);} catch (IOException e) {e.printStackTrace();}if (response == null) {return new HashMap<>();}Map<String, String> responseData = xmlToMap(response);if (isSuccess(responseData)) {// 提现成功,修改记录log.info("微信提现成功: {}", responseData);} else {// 提现失败,更新记录log.error("微信提现过程出错,openid: {}, 错误代码:{} ,错误信息: {}", wechatPay.getWxOpenId(), responseData.get("err_code"), responseData.get("err_code_des"));}return responseData;}/*** 统一下单* document:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1* document:https://pay.weixin.qq.com/static/applyment_guide/applyment_detail_website.shtml* document:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 使用 Native支付 模式2 生成** @param outTradeNo 商户订单号* @param amount     商品金额,单位元* @param subject    商品描述* @param ip         ip地址* @return*/public Map<String, String> unifiedorder(String outTradeNo, String amount, String subject, String ip) {// 请求参数SortedMap<String, String> map = new TreeMap<>();// 设置公共参数setOrderCommonParams(map);// 商户订单号map.put("out_trade_no", outTradeNo);// 商品金额 订单总金额,单位为分map.put("total_fee", new BigDecimal(amount).multiply(new BigDecimal(100)).intValue() + "");// 商品描述map.put("body", subject);// 终端ipmap.put("spbill_create_ip", ip);// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数map.put("notify_url", apiServer + WXPayConstants.NOTIFY_URL);// 交易类型map.put("trade_type", WXPayConstants.TRADE_TYPE_NATIVE);// 设置签名map.put("sign", gennerateSign(map, weChatPayConfig.getAppKey()));// 讲参数转换为微信支付xmlString orderInfo = mapToXml(map);String response = null;try {response = httpRequest(WXPayConstants.UNIFIEDORDER_URL_SUFFIX, orderInfo);} catch (IOException e) {e.printStackTrace();}if (response == null) {return new HashMap<>();}Map<String, String> responseData = xmlToMap(response);if (isSuccess(responseData)) {// 提现成功,修改记录log.info("统一下单成功");} else {// 提现失败,更新记录log.error("统一下单失败, 错误代码:{} ,错误信息: {}", responseData.get("err_code"), responseData.get("err_code_des"));}return responseData;}/*** 查询订单 参数二选一* document:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2** @param transactionId 微信订单号 建议优先使用* @param outTradeNo    商户订单号* @return 查询结果*/public Map<String, String> queryOrder(String transactionId, String outTradeNo) {// 请求参数SortedMap<String, String> map = new TreeMap<>();// 设置公共参数setOrderCommonParams(map);if (StringUtils.isNotBlank(transactionId)) {// 微信订单号map.put("transaction_id", transactionId);} else {// 商户订单号map.put("out_trade_no", outTradeNo);}// 设置签名map.put("sign", gennerateSign(map, weChatPayConfig.getAppKey()));// 讲参数转换为微信支付xmlString orderInfo = mapToXml(map);String response = null;try {response = httpRequest(WXPayConstants.ORDERQUERY_URL_SUFFIX, orderInfo);} catch (IOException e) {e.printStackTrace();}if (response == null) {return new HashMap<>();}Map<String, String> responseData = xmlToMap(response);if (isSuccess(responseData)) {// 提现成功,修改记录log.info("查询成功:{}", responseData);} else {// 提现失败,更新记录log.error("查询订单失败: {}", responseData);}return responseData;}/*** 请求微信支付** @param urlSuffix 请求接口路径后缀* @param xml       请求参数* @return 请求结果* @throws IOException*/public String httpRequest(String urlSuffix, String xml) throws IOException {log.info("请求参数:{}", xml);String content = null;FileInputStream instream = null;CloseableHttpResponse responseEntry = null;try {//指定读取证书格式为PKCS12KeyStore keyStore = KeyStore.getInstance("PKCS12");//判断系统环境String osName = System.getProperty("os.name");log.info("-------系统环境 {} --------", osName);String filePath = null;if (osName.startsWith("Windows")) {// windowsfilePath = ResourceUtils.getURL("classpath:").getPath();} else {// unix or linuxfilePath = System.getProperty("user.dir") + "/";}instream = new FileInputStream(new File(filePath + weChatPayConfig.getCertPath()));//指定PKCS12的密码(商户ID)keyStore.load(instream, weChatPayConfig.getMchId().toCharArray());// Trust own CA and all self-signed certsSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, weChatPayConfig.getMchId().toCharArray()).build();//指定TLS版本, Allow TLSv1 protocol onlySSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);//设置httpclient的SSLSocketFactoryCloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();HttpPost httppost = new HttpPost(WXPayConstants.DOMAIN_API + urlSuffix);//这里要设置编码,不然xml中有中文的话会提示签名失败或者展示乱码httppost.addHeader("Content-Type", "text/xml");StringEntity se = new StringEntity(xml, "UTF-8");httppost.setEntity(se);responseEntry = httpclient.execute(httppost);HttpEntity entity = responseEntry.getEntity();// 获取响应内容content = EntityUtils.toString(entity, "utf-8");// 消耗响应实体,并关闭相关资源占用EntityUtils.consume(entity);} catch (KeyStoreException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (CertificateException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();} catch (UnrecoverableKeyException e) {e.printStackTrace();} finally {if (null != instream) {instream.close();}if (null != responseEntry) {responseEntry.close();}}return content;}/*** 生成签名** @param data 数据* @param key  秘钥* @return 签名*/public static String gennerateSign(Map<String, String> data, String key) {// 将Map转换为TreeMapSet<Map.Entry<String, String>> parameterMapSet = data.entrySet();Iterator<Map.Entry<String, String>> hashMapIterator = parameterMapSet.iterator();Map<String, String> treeMap = new TreeMap<String, String>();while (hashMapIterator.hasNext()) {Map.Entry<String, String> param = hashMapIterator.next();if (!"sign".equals(param.getKey())) {treeMap.put(param.getKey(), param.getValue());}}// 拼接字符串StringBuffer sb = new StringBuffer();Set<Map.Entry<String, String>> treeMapSet = treeMap.entrySet();Iterator<Map.Entry<String, String>> treeMapIterator = treeMapSet.iterator();while (treeMapIterator.hasNext()) {Map.Entry<String, String> param = treeMapIterator.next();// 校验空值if (StringUtils.isEmpty(param.getValue())) {if (treeMapIterator.hasNext()) {} else {sb.replace(sb.toString().length() - 1, sb.toString().length(), "");}continue;}sb.append(param.getKey());sb.append("=");sb.append(param.getValue());if (treeMapIterator.hasNext()) {sb.append("&");}}if (StringUtils.isEmpty(sb.toString())) {throw new RuntimeException("传入的参数为空");}// 拼接keysb.append("&key=").append(key);log.info("微信支付,检验的拼接的字符串: " + sb.toString());String md5Hex = DigestUtils.md5Hex(sb.toString()).toUpperCase();return md5Hex;}/*** 设置公共参数** @param map 参数对象*/public void setCommonParams(SortedMap<String, String> map) {if (null != map) {map.put("mch_appid", weChatPayConfig.getAppId());map.put("mchid", weChatPayConfig.getMchId());// 随机字符串map.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));}}/*** 设置公共参数** @param map 参数对象*/public void setOrderCommonParams(SortedMap<String, String> map) {if (null != map) {map.put("appid", weChatPayConfig.getAppId());// 转账和支付这个字段有区别,转账  mch_id 无下划线map.put("mch_id", weChatPayConfig.getMchId());// 随机字符串map.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));}}/*** 判断请求是否成功** @param response 响应数据* @return true 成功,false 失败*/public boolean isSuccess(Map<String, String> response) {if (null == response) {return false;}if (WXPayConstants.SUCCESS.equals(response.get("return_code")) && WXPayConstants.SUCCESS.equals(response.get("result_code"))) {return true;} else {return false;}}/*** 校验微信支付的sign** @param data 参数数据* @return 校验结果*/public boolean validateSign(Map<String, String> data) {String sign = data.get("sign");log.info("微信支付,传入的sign进行校验 {}", sign);if (StringUtils.isEmpty(sign)) {log.info("微信支付,sign参数为空!");return false;}String md5Hex = gennerateSign(data, weChatPayConfig.getAppKey());if (!md5Hex.equals(sign.toUpperCase())) {log.error("微信支付,签名错误");return false;}return true;}/*** map集合转化成xml字符串** @param data* @return*/public String mapToXml(Map<String, String> data) {try {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();Document document = documentBuilder.newDocument();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();writer.close();return output;} catch (Exception e) {e.printStackTrace();return null;}}/*** xml字符串转化成map集合** @param xml* @return*/public Map<String, String> xmlToMap(String xml) {try {Map<String, String> data = new HashMap<>();DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}stream.close();return data;} catch (Exception e) {e.printStackTrace();return null;}}}

7、充值和回调

package com.server.controller;import com.model.ResultBody;
import com.constatns.WalletConstants;
import com.service.IPayService;
import com.util.CommonUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;/*** 支付 控制器** @author kou*/
@Slf4j
@Validated
@Api(value = "个人钱包", tags = "个人钱包")
@RequestMapping("/pay")
@RestController
public class PayController {@Autowiredprivate IPayService payService;/*** 支付宝收款二维码** @param channel 支付渠道: 1:支付宝, 2:微信* @return 查询结果*/@ApiOperation(value = "支付宝收款二维码", notes = "支付宝收款二维码")@ApiImplicitParams({@ApiImplicitParam(name = "channel", required = true, value = "支付渠道: 1:支付宝, 2:微信", paramType = "form"),@ApiImplicitParam(name = "amount", required = true, value = "金额", paramType = "form"),@ApiImplicitParam(name = "subject", required = true, value = "订单标题", paramType = "form"),})@PostMapping("/qrcode")public ResultBody qrcode(HttpServletRequest request,@NotNull(message = "channel 不能为空") @RequestParam(name = "channel") Integer channel,@NotBlank(message = "amount 不能为空") @RequestParam(name = "amount") String amount,@NotBlank(message = "subject 不能为空") @RequestParam(name = "subject") String subject) {if (channel.equals(WalletConstants.CHANNEL_ALIPAY)) {return payService.alipayQrcode(getUserId(), amount, subject);} else {// 获取IPString ip = CommonUtil.getIpAddr(request);return payService.wxpayQrcode(getUserId(), amount, subject, ip);}}/*** 查询支付宝订单信息** @param tradeNo    支付宝交易号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/@ApiOperation(value = "查询支付宝订单信息", notes = "查询支付宝订单信息")@ApiImplicitParams({@ApiImplicitParam(name = "tradeNo", required = false, value = "支付宝交易号,和商户订单号不能同时为空", paramType = "form"),@ApiImplicitParam(name = "outTradeNo", required = false, value = "订单支付时传入的商户订单号,和支付宝交易号不能同时为空", paramType = "form")})@GetMapping("/query/alipay")public ResultBody queryAlipay(@RequestParam(name = "tradeNo", required = false) String tradeNo,@RequestParam(name = "outTradeNo", required = false) String outTradeNo) {if (StringUtils.isBlank(tradeNo) && StringUtils.isBlank(outTradeNo)) {return ResultBody.failed().msg("参数不能为空");}return payService.queryAlipay(tradeNo, outTradeNo);}/*** 查询微信订单信息** @param tradeNo    微信交易号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/@ApiOperation(value = "查询微信订单信息", notes = "查询微信订单信息")@ApiImplicitParams({@ApiImplicitParam(name = "tradeNo", required = false, value = "微信订单号,和商户订单号不能同时为空", paramType = "form"),@ApiImplicitParam(name = "outTradeNo", required = false, value = "订单支付时传入的商户订单号,和支付宝交易号不能同时为空", paramType = "form")})@GetMapping("/query/wxpay")public ResultBody queryWxpay(@RequestParam(name = "tradeNo", required = false) String tradeNo,@RequestParam(name = "outTradeNo", required = false) String outTradeNo) {if (StringUtils.isBlank(tradeNo) && StringUtils.isBlank(outTradeNo)) {return ResultBody.failed().msg("参数不能为空");}return payService.queryWxpay(tradeNo, outTradeNo);}/*** 支付宝证书支付异步通知* https://opendocs.alipay.com/open/194/103296** @param request 通知数据* @return 通知结果*/@RequestMapping("/notify/alipay")public String alipayNotify(HttpServletRequest request) {log.info("-----------支付宝支付异步通知----------------");Map requestParams = request.getParameterMap();log.info("支付宝回调参数:" + requestParams);Map<String, String> params = new HashMap<>();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);}log.info("支付宝回调参数解析:" + params);return payService.alipayNotifyDealWithData(params);}/*** 微信支付异步通知* document: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8** @param request 通知数据* @return 通知结果*/@RequestMapping("/notify/wxpay")public void wxpayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {log.info("-----------微信支付异步通知----------------");BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));String line = null;// 返回的xml数据StringBuilder responseXml = new StringBuilder();while ((line = br.readLine()) != null) {responseXml.append(line);}br.close();log.info("微信支付异步通知响应参数:{}", responseXml.toString());// 处理数据String result = payService.wxpayNotifyDealWithData(responseXml.toString());log.info("处理结果:{}", result);StringBuffer resXml = new StringBuffer("<xml>");if (result.equals(WalletConstants.SUCCESS)) {resXml.append("<return_code><![CDATA[SUCCESS]]></return_code>");resXml.append("<return_msg><![CDATA[OK]]></return_msg>");} else {resXml.append("<return_code><![CDATA[FAIL]]></return_code>");resXml.append("<return_msg><![CDATA[").append(result).append("]]></return_msg>");}resXml.append("</xml>");log.info("返回微信通知结果:{}", resXml.toString());BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());out.write(resXml.toString().getBytes());out.flush();out.close();}}
package com.server.service;import com.model.ResultBody;import java.util.Map;/*** 支付业务实现类** @author kou*/
public interface IPayService {/*** 生成支付宝收款二维码** @param userId  用户id* @param amount  金额* @param subject 主体* @return 二维码地址*/ResultBody alipayQrcode(Long userId, String amount, String subject);/*** 生成收款二维码** @param userId  用户id* @param amount  金额* @param subject 主体* @param ip      IP地址* @return 二维码地址*/ResultBody wxpayQrcode(Long userId, String amount, String subject, String ip);/*** 查询支付宝订单信息** @param tradeNo    支付宝交易号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/ResultBody queryAlipay(String tradeNo, String outTradeNo);/*** 查询微信订单信息** @param transactionId    微信订单号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/ResultBody queryWxpay(String transactionId, String outTradeNo);/*** 支付宝异步通知处理数据** @param params 通知数据* @return 结果*/String alipayNotifyDealWithData(Map<String, String> params);/*** 微信异步通知处理数据** @param xml 通知数据* @return 结果*/String wxpayNotifyDealWithData(String xml);}
package com.opencloud.wallet.server.service.impl;import com.alipay.api.AlipayApiException;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.model.ResultBody;
import com.constatns.WalletConstants;
import com.model.entity.TradeBill;
import com.service.IPayService;
import com.util.AlipayTransferUtil;
import com.util.WeiXinTransfersUtil;
import com.util.alipay.AliPayConstants;
import com.util.wxpay.WXPayConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;/*** 支付业务实现类** @author kou*/
@Slf4j
@Service
public class PayServiceImpl implements IPayService {@Autowiredprivate TradeBillService tradeBillService;@Autowiredprivate AlipayTransferUtil alipayTransferUtil;@Autowiredprivate WeiXinTransfersUtil weiXinTransfersUtil;@Autowiredprivate RechargeLogService rechargeLogService;/*** 生成收款二维码** @param userId  用户id* @param amount  金额* @param subject 主体* @return 二维码地址*/@Overridepublic ResultBody alipayQrcode(Long userId, String amount, String subject) {try {TradeBill bill = generateBill(userId, amount);bill.setChannel(WalletConstants.CHANNEL_ALIPAY);// 调用支付宝生成收款二维码AlipayTradePrecreateResponse response = alipayTransferUtil.precreate(bill.getOutTradeNo(), amount, subject);// 判断是否成功if (response.isSuccess()) {// 保存收款二维码信息到交易订单中bill.setQrCode(response.getQrCode());int ret = tradeBillService.saveTradeBill(bill);if (ret > 0) {return ResultBody.ok().data(response);}return ResultBody.failed().msg("二维码生成失败,请联系管理员!");} else {log.error("生成支付宝二维码失败,code: {}, msg:{}, body:{}", response.getSubCode(), response.getSubMsg(), response.getBody());return ResultBody.failed().msg(response.getSubMsg());}} catch (AlipayApiException e) {e.printStackTrace();} catch (JsonProcessingException e) {e.printStackTrace();}log.error("生成二维码错误");return ResultBody.failed().msg("二维码生成失败,请联系管理员!");}/*** 生成收款二维码** @param userId  用户id* @param amount  金额* @param subject 主体* @param ip      IP地址* @return 二维码地址*/@Overridepublic ResultBody wxpayQrcode(Long userId, String amount, String subject, String ip) {TradeBill bill = generateBill(userId, amount);// 调用微信生成收款二维码bill.setChannel(WalletConstants.CHANNEL_WEIXIN);// 统一下单Map<String, String> response = weiXinTransfersUtil.unifiedorder(bill.getOutTradeNo(), amount, subject, ip);if (weiXinTransfersUtil.isSuccess(response)) {// 下单成功log.info("------------- 统一下单成功,下单信息:{}", response);bill.setQrCode(response.get("code_url"));bill.setPrepayId(response.get("prepay_id"));// 保存收款二维码信息到交易订单中int ret = tradeBillService.saveTradeBill(bill);if (ret > 0) {response.put("outTradeNo", bill.getOutTradeNo());return ResultBody.ok().data(response);}return ResultBody.failed().msg("二维码生成失败,请联系管理员!");} else {// 下单失败log.error("统一下单失败, 失败信息: {}", response);return ResultBody.failed().msg(response.get("err_code_des"));}}/*** 查询支付宝订单信息** @param tradeNo    支付宝交易号,和商户订单号不能同时为空* @param outTradeNo 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/@Overridepublic ResultBody queryAlipay(String tradeNo, String outTradeNo) {log.info("查询订单,tradeNo: {} , outTradeNo: {}", tradeNo, outTradeNo);try {AlipayTradeQueryResponse response = alipayTransferUtil.query(tradeNo, outTradeNo);// 判断是否成功if (response.isSuccess()) {TradeBill bill = null;if (StringUtils.isNotBlank(outTradeNo)) {bill = tradeBillService.queryByOutTradeNo(outTradeNo);} else if (StringUtils.isNotBlank(tradeNo)) {bill = tradeBillService.queryByTradeNo(tradeNo);}if (null != bill) {// 订单状态等于null或者不等于成功,更新状态if (StringUtils.isBlank(bill.getTradeStatus()) || !"TRADE_SUCCESS".equals(bill.getTradeStatus())) {// 判断数据库数据状态和返回的状态是否一致,一致则不更新if (StringUtils.isNotBlank(bill.getTradeStatus()) && bill.getTradeStatus().equals(response.getTradeStatus())) {log.info("当前状态已同步,禁止再次同步");return ResultBody.ok().data(response);}// 获取当前账单状态String billTradeStatus = bill.getTradeStatus();log.info("订单信息未同步成功,当前订单状态:{},开始同步订单信息", billTradeStatus);// 更新订单信息// 订单号 支付宝交易号bill.setTradeNo(response.getTradeNo());// 交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)bill.setTradeStatus(response.getTradeStatus());// 买家支付宝账号bill.setBuyerLogonId(response.getBuyerLogonId());bill.setBuyerId(response.getBuyerUserId());bill.setTotalAmount(new BigDecimal(response.getTotalAmount()));boolean ret = tradeBillService.updateById(bill);if (ret) {log.info("更新账单表成功!商户订单号: {}", outTradeNo);// 判断交易是否成功,成功则更新用户金额,防止多次更新金额if (AliPayConstants.TRADE_STATUS_SUCCESS.equals(response.getTradeStatus())) {// 付款成功才更新用户金额if (!AliPayConstants.TRADE_STATUS_SUCCESS.equals(billTradeStatus)) {log.info("更新用户金额成功!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());} else {log.info("账单已被更新过,只更新账单信息!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());}} else {log.info("用户未成功付款,禁止更新用户金额,支付状态:{}", response.getTradeStatus());}}} else {log.info("订单已被更新过,禁止再次更新");}} else {log.error("支付宝订单已更新,code: {}, msg:{}", response.getSubCode(), response.getSubMsg());}return ResultBody.ok().data(response);} else {log.error("查询支付宝订单失败,code: {}, msg:{}", response.getSubCode(), response.getSubMsg());return ResultBody.ok().data(response);}} catch (AlipayApiException e) {log.error(e.getMessage(), e);return ResultBody.failed().msg("查询失败");}}/*** 查询微信订单信息** @param transactionId 微信订单号,和商户订单号不能同时为空* @param outTradeNo    订单支付时传入的商户订单号,和支付宝交易号不能同时为空。trade_no,out_trade_no如果同时存在优先取trade_no* @return 查询结果*/@Overridepublic ResultBody queryWxpay(String transactionId, String outTradeNo) {log.info("查询订单,transactionId: {} , outTradeNo: {}", transactionId, outTradeNo);Map<String, String> response = weiXinTransfersUtil.queryOrder(transactionId, outTradeNo);// 判断是否成功if (weiXinTransfersUtil.isSuccess(response)) {TradeBill bill = null;if (StringUtils.isNotBlank(transactionId)) {bill = tradeBillService.queryByTradeNo(transactionId);} else if (StringUtils.isNotBlank(outTradeNo)) {bill = tradeBillService.queryByOutTradeNo(outTradeNo);}if (null != bill) {// 订单状态等于null或者不等于成功,更新状态if (StringUtils.isBlank(bill.getTradeStatus()) || !WXPayConstants.SUCCESS.equals(bill.getTradeStatus())) {// 判断数据库数据状态和返回的状态是否一致,一致则不更新if (StringUtils.isNotBlank(bill.getTradeStatus()) && bill.getTradeStatus().equals(response.get("trade_state"))) {log.info("当前状态已同步,禁止再次同步");return ResultBody.ok().data(response);}// 获取当前账单状态String billTradeStatus = bill.getTradeStatus();log.info("订单信息未同步成功,当前订单状态:{},开始同步订单信息", billTradeStatus);// 更新订单信息// 微信订单号bill.setTradeNo(response.get("transaction_id"));if (StringUtils.isNotBlank(response.get("total_fee"))) {bill.setTotalAmount(new BigDecimal(response.get("total_fee")));}// 支付完成时间DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");if (StringUtils.isNotBlank(response.get("time_end"))) {LocalDateTime PaymentDate = LocalDateTime.parse(response.get("time_end"), df);bill.setPaymentTime(Date.from(PaymentDate.atZone(ZoneId.systemDefault()).toInstant()));}bill.setTradeStatus(response.get("trade_state"));boolean ret = tradeBillService.updateById(bill);if (ret) {log.info("更新账单表成功!商户订单号: {}", outTradeNo);// 判断交易是否成功,成功则更新用户金额,防止多次更新金额if (WXPayConstants.SUCCESS.equals(bill.getTradeStatus())) {// 付款成功才更新用户金额if (!WXPayConstants.SUCCESS.equals(billTradeStatus)) {log.info("开始更新用户金额,商户订单号: {},用户id {}", outTradeNo, bill.getUserId());log.info("更新用户金额成功!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());} else {log.info("账单已被更新过,只更新账单信息!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());}} else {log.info("用户未成功付款,禁止更新用户金额,支付状态:{}", bill.getTradeStatus());}}} else {log.info("订单已被更新过,禁止再次更新");}} else {log.error("订单已更新,transactionId: {} , outTradeNo: {}", transactionId, outTradeNo);}return ResultBody.ok().data(response);} else {log.error("查询微信订单失败:", response);return ResultBody.ok().data(response);}}/*** 异步通知处理数据** @param params 异步通知参数*/@Transactional(rollbackFor = Exception.class)@Overridepublic String alipayNotifyDealWithData(Map<String, String> params) {try {// 校验请求是否合法boolean validated = alipayTransferUtil.validateCert(params);if (validated) {// 校验成功log.info("支付宝回调签名认证成功,开始更新账单数据");// 商户订单号String outTradeNo = params.get("out_trade_no");String tradeStatus = params.get("trade_status");log.info("开始处理支付宝回调业务,支付宝交易状态:{},params:{}", tradeStatus, params);TradeBill bill = tradeBillService.queryByOutTradeNo(outTradeNo);if (null == bill) {log.info("账单信息为空,商户订单号: {}", outTradeNo);return "failure";}// 获取当前账单状态String billTradeStatus = bill.getTradeStatus();// 订单号 支付宝交易号bill.setTradeNo(params.get("trade_no"));// 交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)bill.setTradeStatus(tradeStatus);// 买家支付宝账号bill.setBuyerLogonId(params.get("buyer_logon_id"));bill.setBuyerId(params.get("buyer_id"));bill.setTotalAmount(new BigDecimal(params.get("total_amount")));bill.setReceiptAmount(new BigDecimal(params.get("receipt_amount")));DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");if (StringUtils.isNotBlank(params.get("gmt_create"))) {LocalDateTime transDate = LocalDateTime.parse(params.get("gmt_create"), df);bill.setTransTime(Date.from(transDate.atZone(ZoneId.systemDefault()).toInstant()));}if (StringUtils.isNotBlank(params.get("gmt_payment"))) {LocalDateTime PaymentDate = LocalDateTime.parse(params.get("gmt_payment"), df);bill.setPaymentTime(Date.from(PaymentDate.atZone(ZoneId.systemDefault()).toInstant()));}int ret = tradeBillService.updateTradeBillByOutTradeNo(bill);if (ret > 0) {log.info("更新账单表成功!商户订单号: {}", outTradeNo);// 判断交易是否成功,成功则更新用户金额,防止多次更新金额if (AliPayConstants.TRADE_STATUS_SUCCESS.equals(tradeStatus)) {// 付款成功才更新用户金额if (!AliPayConstants.TRADE_STATUS_SUCCESS.equals(billTradeStatus)) {log.info("开始更新用户金额,商户订单号: {},用户id {}", outTradeNo, bill.getUserId());log.info("更新用户金额成功!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());return WalletConstants.SUCCESS;} else {log.info("账单已被更新过,只更新账单信息!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());return WalletConstants.SUCCESS;}} else {log.info("用户未成功付款,禁止更新用户金额,支付状态:{}", tradeStatus);}} else {log.error("更新账单表失败!商户订单号: {}", outTradeNo);}} else {log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params);}} catch (Exception e) {log.error(e.getMessage(), e);log.info("支付宝回调签名认证失败,signVerified=false, params:{}", params);}return WalletConstants.FAILURE;}/*** 微信异步通知处理数据* document: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8** @param xml 通知数据* @return 结果*/@Transactional(rollbackFor = Exception.class)@Overridepublic String wxpayNotifyDealWithData(String xml) {// 处理结果,返回给微信String resXml = WalletConstants.FAILURE;// 将xml 转为为mapMap<String, String> params = weiXinTransfersUtil.xmlToMap(xml);log.info("xml 转为 map, map = {}", params);// 验证签名是否正确boolean validate = weiXinTransfersUtil.validateSign(params);if (validate) {log.info("------- 微信签名校验成功 --------");// 判断响应结果是否成功if (weiXinTransfersUtil.isSuccess(params)) {log.info("------- 准备更新用户账单数据");// 更新账单数据// 商户订单号String outTradeNo = params.get("out_trade_no");TradeBill bill = tradeBillService.queryByOutTradeNo(outTradeNo);if (null == bill) {log.info("账单信息为空,商户订单号: {}", outTradeNo);return "未查询到商户订单号";}// 获取当前账单状态String billTradeStatus = bill.getTradeStatus();// 微信支付订单号bill.setTradeNo(params.get("transaction_id"));bill.setTotalAmount(new BigDecimal(params.get("total_fee")));// bill.setTradeStatus(params.get("trade_state"));bill.setTradeStatus(WXPayConstants.SUCCESS);// 支付完成时间DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");if (StringUtils.isNotBlank(params.get("time_end"))) {LocalDateTime PaymentDate = LocalDateTime.parse(params.get("time_end"), df);bill.setPaymentTime(Date.from(PaymentDate.atZone(ZoneId.systemDefault()).toInstant()));}int ret = tradeBillService.updateTradeBillByOutTradeNo(bill);if (ret > 0) {log.info("更新账单表成功!商户订单号: {}", outTradeNo);// 判断交易是否成功,成功则更新用户金额,防止多次更新金额// 判断账单是否已经被更新if (!WXPayConstants.SUCCESS.equals(billTradeStatus)) {log.info("开始更新用户金额,商户订单号: {},用户id {}", outTradeNo, bill.getUserId());log.info("更新用户金额成功!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());resXml = WalletConstants.SUCCESS;} else {log.info("账单已被更新过,只更新账单信息!商户订单号: {},用户id {}", outTradeNo, bill.getUserId());resXml = WalletConstants.SUCCESS;}} else {log.error("更新账单表失败!商户订单号: {}", outTradeNo);resXml = WalletConstants.FAILURE;}} else {log.error("------- 接收到的支付结果失败状态,结果: {}", params);resXml = "未收到微信成功状态码";}} else {log.error("------- 微信签名校验失败 --------");resXml = "签名失败";}return resXml;}/*** 生成订单信息** @param userId* @param amount* @return 订单信息*/private TradeBill generateBill(Long userId, String amount) {String outTradeNo = tradeBillService.generateOutTradeNo(userId);TradeBill bill = new TradeBill();bill.setOutTradeNo(outTradeNo);bill.setType(1);bill.setUserId(userId);bill.setAmount(new BigDecimal(amount));Date date = new Date();bill.setCreateTime(date);return bill;}}

支付宝微信充值和提现相关推荐

  1. 仿支付宝微信提现输入判断

    下面是支付宝,微信充值要求的可以输入什么值不可以输入什么值要求,保留俩位有效数字,其他等等 mMoney.addTextChangedListener(new TextWatcher() { @Ove ...

  2. 微信的充值和提现是什么意思

    在微信中,提现一般指把微信上的钱提现到银行卡中:而充值是将绑定在微信app中银行卡里的钱充入微信钱包,方便随时使用,可以用来发红包.充值话费.向商家付款等等.微信上的钱提现到银行卡中时,微信会收取0. ...

  3. 最新支付宝/微信免费提现攻略

    微信发布公告称,将在8月1日停止免费信用卡还款,届时每笔还款都有0.1%的手续费,好在信用卡的还款方式多种多样,不过说起自己支付宝和微信钱包里的钱,许多小伙伴正卡在提现手续费这一关上,对此我也是郁闷很 ...

  4. php如何实现余额充值,请问应用中实现绑定银行卡并进行消费、充值、提现一般是怎么搞的?...

    我现在的系统中要实现一个个人账户,因此希望能够满足充值.提现.消费等业务,不知道怎么搞法比较好,是不是要和第三方金融支付平台合作的.我看了下,还有像饿了么这种APP,不绑定银行卡,充值的时候就支持微信 ...

  5. 6月30日后支付宝还能正常提现吗?因为银行直连要停止了

    3月末网联公布了网联涵(2018)42号文件,其名称为"关于非银行支付机构网络支付清算平台渠道接入工作相关事宜的函". 在这份文件中声明中,自2018年6月30日起,银行将关闭与第 ...

  6. 支付宝微信的数字经营项目是风口还是割韭菜(带项目评测)

    大部分创业者存在一个误区,就是忽视创业前的市场调研,尤其是通过加盟形式代理产品时.往往只听人家一面之词,就糊里糊涂去找店铺.装修.找员工.开始营业,最终的结果多半是亏本转让或者艰难维继. 而我现在要做 ...

  7. 个人支付宝/微信/云闪付/商户聚合码/银行卡等到账通知常用技术方案总结

    个人支付宝/微信/云闪付/商户聚合码/银行卡等到账通知常用技术方案总结 通过分析和研究,以及观察目前市面上面已经存在的相关技术方案平台,结合自己最终的实现,整体方案实现,常用架构如下(大部分的只实现了 ...

  8. php 微信提现,微信支付商户提现功能介绍

    微信支付商户提现功能介绍 微信支付商户提现功能介绍 1)提现产品简介 提现作为资金流转基础功能,为商户提供将资金从商户平台提现至商户号关联银行卡的能力,分为自动提现和手动提现.自动提现为系统自动执行, ...

  9. 梦幻诛仙内充php怎么使用,新手教程 梦幻诛仙手游微信充值步骤分享

    新手教程 梦幻诛仙手游微信充值步骤分享 梦幻诛仙手游苹果充值是不能通过微信和支付宝来支付的,相对于安卓版本就要麻烦许多,鉴于不少玩家都有使用支付宝和微信,这里本文就为大家详细介绍下梦幻诛仙手游用微信和 ...

  10. VUE+element-ui微信充值支付

    VUE+element-ui微信充值支付 前端页面是参考别人的 前端页面是参考别人的 链接:http://www.lanlanwork.com/blog/?post=5839 <template ...

最新文章

  1. pcb入门之新建工程
  2. python编程头文件_python头文件怎么写
  3. java填空题 在非静态成员方法中_Java程序设计填空和改错题(参考答案)
  4. 黑马程序员-Java基础-正则表达式
  5. pyecharts geo_pyechartstableau可视化分析案例+分析思路
  6. android 心跳效果动画,Android实现心跳的效果
  7. 桥接网络,nat网络,静态IP配置,相关命令
  8. 【校园电子书城】测试及部署
  9. Tortoise SVN 如何汉化(最简单的处理方式,一看就会)
  10. flying saucer技术生成pdf文档
  11. Navicat安装与破解
  12. Android从Assets复制文件到本地
  13. 弹性碰撞次数与圆周率的关系 - 3Blue1Brown
  14. Arcgis实例学习5--统计直方图、空间分布图、统计信息
  15. 认识计算机软件的教学过程,《认识计算机》教学设计
  16. Axis2 报错 Faulty Services
  17. 学生党如何拿到阿里技术offer
  18. 华为2019算法大赛CTR预估数据探索
  19. UCOS-II优先级调度算法之详解OSPrioHighRdy=(INT 8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);
  20. linux指令(一、目录与档案)

热门文章

  1. 穷举法破解密码-方法详解
  2. ps添加的阴影怎么去除_ps怎么可以把阴影去除
  3. android编程微博的发送,基于Android手机微博客户端的开发.doc
  4. 计算机老师教师节祝福语,教师节祝福语大全简短
  5. vue加d3js实现3d饼图
  6. L9110H电机驱动模块-FPGA
  7. Google地图中关于根据具体坐标定位真实地理位置
  8. mysql 查询生日_MySQL,怎么查询一段时间内过生日的员工
  9. oss上传判断_React实现阿里云OSS上传文件的示例
  10. 音频处理——常用音频编码格式简介(PCM、G726、ADPCM、LPCM、G711、AAC)