利用微信小程序+JAVA實現(xiàn)微信支付的全過程
本文主要講的是小程序實現(xiàn)微信支付功能,后臺采用JAVA。
一.準備工作
1.小程序
2.微信商戶號
1.商戶號申請
這里對小程序的申請不做贅述。
如果沒有微信商戶號的同學,點擊該鏈接https://pay.weixin.qq.com/,按照下屬步驟進行商戶號申請。
掃碼之后點擊"成為商家",這里主要有個體工商戶和企業(yè),按照事實填寫,然后按照步驟填寫就行了。
主要需要營業(yè)執(zhí)照,法人信息,公賬信息等。
2.微信商戶號關聯(lián)小程序
點擊"產品中心"的"開發(fā)配置",點擊"新增授權申請單"。
輸入你的小程序appid,點擊下一步。
然后到小程序后臺>微信支付>商戶號管理里,會出現(xiàn)一個申請單,點擊“查看”。
點擊確認綁定,這樣你的商戶號就與小程序進行綁定了。
二.代碼編寫
1.小程序
小程序這塊主要是調用一下后臺接口獲取參數(shù),然后通過參數(shù)拉起微信支付。
orderPay(payInfo){ let that = this wx.requestPayment({ 'timeStamp': payInfo.timeStamp, 'nonceStr': payInfo.nonceStr, 'package': payInfo.package, 'signType': payInfo.signType, 'paySign': payInfo.paySign, 'success': function (res) { // 支付成功的回調 }, 'fail': function (res) { console.log(JSON.stringify(res)); wx.showToast({title: '支付失敗', icon: 'none',duration: 2000,mask: true}) } }) },
這里的payInfo就是從后臺接口獲取的支付參數(shù),通過wx.requestPayment就可以拉起微信支付了。具體的參數(shù)信息在下面會進行講解。
2.服務端(JAVA)
服務端這邊主要是三個接口:
1.提交支付訂單
這個主要是為了獲取提交支付訂單,獲取前端拉起支付的參數(shù)。
2.微信支付回調
當你小程序拉起支付并且成功支付后,會將支付結果回調到這個接口
3.支付訂單查詢
你也可以主動通過訂單號查詢支付訂單狀態(tài)
下面是我的代碼,包含了我的業(yè)務代碼,大家將就著看吧
controller:
import com.smart.iot.gmt.app.bean.CommonReponse; import com.smart.iot.gmt.app.constant.ResponseContant; import com.smart.iot.gmt.app.constant.SessionKeyConstants; import com.smart.iot.gmt.app.request.wechatPay.PaymentRequest; import com.smart.iot.gmt.app.request.wechatPay.QueryPayOrderRequest; import com.smart.iot.gmt.app.response.wechat.WechatPaymentResponse; import com.smart.iot.gmt.app.response.wechat.WechatQueryPayOrderResponse; import com.smart.iot.gmt.app.service.CommonService; import com.smart.iot.gmt.app.service.pay.wechat.WxPayService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Slf4j @RestController public class WxPayController { @Autowired private WxPayService service; @Autowired private CommonService commonService; /** * * @author Rick chou * @date 2024/7/18 15:33 * 提交支付 * */ @PostMapping("/payment") public CommonReponse payment(@RequestBody PaymentRequest paymentRequest) { Integer price = paymentRequest.getPrice(); String orderId = paymentRequest.getOrderId(); Map<String, Object> result = service.payment(price, orderId, userId); CommonReponse commonResponse = commonService.getCommonResponse(ResponseContant.SUCCESS_CODE, ResponseContant.SUCCESS, result); return new WechatPaymentResponse(commonResponse,result); } /** * * @author Rick chou * @date 2024/7/15 16:57 * 微信支付回調 * */ @PostMapping("/payNotify") public void payNotify(HttpServletRequest request) throws Exception { service.payNotify(request); } /** * * @author Rick chou * @date 2024/7/15 16:57 * 支付查詢 * */ @PostMapping("/queryPayOrder") public CommonReponse queryPayOrder(@RequestBody QueryPayOrderRequest request) { Map<String, Object> result = service.queryPayOrder(request); CommonReponse commonResponse = commonService.getCommonResponse(ResponseContant.SUCCESS_CODE, ResponseContant.SUCCESS, result); return new WechatQueryPayOrderResponse(commonResponse,result); } }
service:
import com.alibaba.fastjson.JSON; import com.smart.iot.constant.RedisKeys; import com.smart.iot.device.dto.LockNotifyMessageDTO; import com.smart.iot.device.service.DeviceRedisCacheService; import com.smart.iot.gmt.app.annotation.SpringUtil; import com.smart.iot.gmt.app.bo.*; import com.smart.iot.gmt.app.entity.MemberOrderDetailEntity; import com.smart.iot.gmt.app.enums.MemberOrderStateEnum; import com.smart.iot.gmt.app.request.wechatPay.QueryPayOrderRequest; import com.smart.iot.gmt.app.service.LockNotifyMessageService; import com.wechat.pay.java.service.payments.model.Transaction; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; import java.util.Map; @Slf4j @Service public class WxPayService { public Map<String, Object> payment(Integer price, String orderId, String userId){ Map<String, Object> result = WechatPayBo.payment(price, orderId, userId); return result; } @Transactional public void payNotify(HttpServletRequest request) throws Exception { Transaction parse = WechatPayBo.payNotify(request); updateAccountDetail(parse,false); } @Transactional public Map<String,Object> queryPayOrder(QueryPayOrderRequest request) { String orderId = request.getOrderId(); Transaction parse = WechatPayBo.queryPayOrder(orderId); return updateAccountDetail(parse,true); } /** * * @author Rick chou * @date 2024/7/16 11:03 * 支付回調處理 * 1.更新訂單狀態(tài) * 2.添加支付記錄 * 3.通知小程序 * */ public Map<String,Object> updateAccountDetail(Transaction parse, boolean active) { String orderId = parse.getOutTradeNo(); Transaction.TradeStateEnum tradeState = parse.getTradeState(); if(tradeState==Transaction.TradeStateEnum.SUCCESS) { if(OrderBo.check(orderId).getState() == MemberOrderStateEnum.IN_PROGRESS.code) { OrderBo order = this.updateOrder(orderId); this.renewMember(orderId); this.saveRecord(parse, order.getUserId()); } } if(active){ return JSON.parseObject(buildParse(parse),Map.class); }else{ this.payNoticeMessage(parse,orderId); } return null; } /** * * @author Rick chou * @date 2024/7/19 15:31 * 更新訂單狀態(tài) * */ private OrderBo updateOrder(String orderId){ OrderBo order = OrderBo.check(orderId); order.finish(); String key = RedisKeys.ADD_ORDER_PAY_TIME_PREFIX + orderId; SpringUtil.getBean(DeviceRedisCacheService.class).delete(key); return order; } /** * * @author Rick chou * @date 2024/7/19 15:32 * 更新會員時間 * */ private void renewMember(String orderId){ MemberOrderDetailEntity orderDetail = OrderDetailBo.getByOrderId(orderId); UserMemberBo.renew(orderDetail); } /** * * @author Rick chou * @date 2024/7/19 15:33 * 保存支付記錄 * */ private void saveRecord(Transaction parse,String userId){ String orderId = parse.getOutTradeNo(); Integer amount = parse.getAmount().getTotal(); String tradeType = parse.getTradeType().name(); PayRecordBo.create(userId,orderId,amount,tradeType); } /** * * @author Rick chou * @date 2024/7/19 15:38 * 將支付結果下發(fā)小程序 * */ private void payNoticeMessage(Transaction parse,String orderId){ MemberOrderDetailEntity detail = OrderDetailBo.getByOrderId(orderId); LockNotifyMessageService service = SpringUtil.getBean(LockNotifyMessageService.class); LockNotifyMessageDTO dto = new LockNotifyMessageDTO(); dto.setLockId(detail.getDeviceId()); dto.setMessageParams(buildParse(parse)); service.dealPayResultNotifyMessage(dto); } private String buildParse(Transaction parse){ parse.setMchid(null); parse.setAppid(null); parse.setBankType(null); parse.setBankType(null); parse.setAttach(null); return JSON.toJSONString(parse); } }
bo:
import com.smart.iot.gmt.app.annotation.SpringUtil; import com.smart.iot.gmt.app.entity.PayRecordEntity; import com.smart.iot.gmt.app.service.pay.wechat.PayInfoConfig; import com.smart.iot.gmt.app.service.pay.wechat.WXPayUtil; import com.smart.iot.util.StringUtil; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import com.wechat.pay.java.core.exception.ServiceException; import com.wechat.pay.java.core.exception.ValidationException; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.service.payments.jsapi.JsapiService; import com.wechat.pay.java.service.payments.jsapi.model.*; import com.wechat.pay.java.service.payments.model.Transaction; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.wechat.pay.java.core.http.Constant.*; /** * * @author Rick chou * @date 2024/7/16 9:10 * 微信支付BO * */ @Slf4j @Component public class WechatPayBo extends PayRecordEntity { private static PayInfoConfig getConfig(){ PayInfoConfig payInfoConfig = SpringUtil.getBean(PayInfoConfig.class); return payInfoConfig; } /** * * @author Rick chou * @date 2024/7/16 9:35 * 構建支付請求SERVICE * */ public static JsapiService getService(){ PayInfoConfig payInfoConfig = getConfig(); Config config = new RSAAutoCertificateConfig.Builder() .merchantId(payInfoConfig.getMchId()) .privateKeyFromPath(payInfoConfig.getKeyPath()) .merchantSerialNumber(payInfoConfig.getMchSerialNo()) .apiV3Key(payInfoConfig.getApiKey()) .build(); JsapiService service = new JsapiService.Builder().config(config).build(); return service; } /** * * @author Rick chou * @date 2024/7/16 10:31 * 構造NOTIFY_CONFIG * */ private static NotificationConfig buildNotifyConfig(){ PayInfoConfig payInfoConfig = getConfig(); NotificationConfig config = new RSAAutoCertificateConfig.Builder() .merchantId(payInfoConfig.getMchId()) .privateKeyFromPath(payInfoConfig.getKeyPath()) .merchantSerialNumber(payInfoConfig.getMchSerialNo()) .apiV3Key(payInfoConfig.getApiKey()) .build(); return config; } /** * * @author Rick chou * @date 2024/7/16 9:35 * 構建支付請求參數(shù) * */ private static PrepayRequest buildParam(Integer price, String orderId, String userId){ PayInfoConfig payInfoConfig = getConfig(); PrepayRequest prepayRequest = new PrepayRequest(); Amount amount = new Amount(); amount.setTotal(price); prepayRequest.setAmount(amount); prepayRequest.setAppid(payInfoConfig.getAppId()); prepayRequest.setMchid(payInfoConfig.getMchId()); prepayRequest.setNotifyUrl(payInfoConfig.getNotifyUrl()); // 回調接口地址 prepayRequest.setDescription("微信支付"); prepayRequest.setOutTradeNo(orderId); // 訂單號 prepayRequest.setAttach("member"); // 訂單類型(回調時可根據(jù)這個數(shù)據(jù)辨別訂單類型或其他) //根據(jù)token拿到openid,指定該預支付訂單的支付者身份 Payer payer = new Payer(); payer.setOpenid(WeixinUserBo.getOpenId(userId)); prepayRequest.setPayer(payer); return prepayRequest; } /** * * @author Rick chou * @date 2024/7/16 9:53 * 解析支付結果 * */ private static Map<String,Object> parsePay(PrepayResponse response){ PayInfoConfig payInfoConfig = getConfig(); Map<String, Object> params = new HashMap<>(); Long timeStamp = System.currentTimeMillis() / 1000; String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32); String signatureStr = Stream.of( payInfoConfig.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId() ).collect(Collectors.joining("\n", "", "\n")); String sign = WXPayUtil.getSign(signatureStr, payInfoConfig.getKeyPath()); params.put("timeStamp", String.valueOf(timeStamp)); params.put("nonceStr", substring); params.put("paySign", sign); params.put("signType", "RSA"); params.put("package", "prepay_id=" + response.getPrepayId()); return params; } /** * * @author Rick chou * @date 2024/7/16 10:33 * 解析回調結果 * */ private static RequestParam parseNotify(HttpServletRequest request)throws IOException { String data = StringUtil.getStringForInput(request.getInputStream()); String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP); String nonce = request.getHeader(WECHAT_PAY_NONCE); String signType = request.getHeader("Wechatpay-Signature-Type"); String serialNo = request.getHeader(WECHAT_PAY_SERIAL); String signature = request.getHeader(WECHAT_PAY_SIGNATURE); RequestParam requestParam = new RequestParam.Builder() .serialNumber(serialNo) .nonce(nonce) .signature(signature) .timestamp(timestamp) .signType(signType) // 若未設置signType,默認值為 WECHATPAY2-SHA256-RSA2048 .body(data) .build(); return requestParam; } /** * * @author Rick chou * @date 2024/7/16 9:47 * 調起支付 * */ public static Map<String, Object> payment(Integer price, String orderId, String userId){ JsapiService service = getService(); PrepayRequest prepayRequest = buildParam(price, orderId, userId); PrepayResponse response = service.prepay(prepayRequest); Map<String, Object> result = parsePay(response); result.put("orderId",orderId); return result; } /** * * @author Rick chou * @date 2024/7/16 10:16 * 支付回調 * */ public static Transaction payNotify(HttpServletRequest request) throws Exception { NotificationConfig config = buildNotifyConfig(); NotificationParser parser = new NotificationParser(config); RequestParam requestParam = parseNotify(request); Transaction parse = null; try { parse = parser.parse(requestParam, Transaction.class); }catch (ValidationException e){ log.error("sign verification failed", e); } return parse; } /** * * @author Rick chou * @date 2024/7/16 11:17 * 查詢訂單 * */ public static Transaction queryPayOrder(String orderId) { PayInfoConfig payInfoConfig = getConfig(); JsapiService service = getService(); QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest(); queryRequest.setMchid(payInfoConfig.getMchId()); queryRequest.setOutTradeNo(orderId); Transaction parse = null; try { parse = service.queryOrderByOutTradeNo(queryRequest); }catch (ServiceException e){ log.info("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage()); log.info("reponse body=[%s]\n", e.getResponseBody()); } return parse; } }
PayInfoConfig
import lombok.Data; import lombok.ToString; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @ToString @Component @ConfigurationProperties(prefix = "wx") public class PayInfoConfig { //小程序appid private String appId; //商戶號 private String mchId; //證書序列號 private String mchSerialNo; //小程序秘鑰 private String appSecret; //api秘鑰 private String apiKey; //回調接口地址 private String notifyUrl; //證書地址 private String keyPath; }
上述的PayInfoConfig中的參數(shù)第二、三、五、七個參數(shù)去商戶號后臺獲取。
三.補充說明
在實際的支付開發(fā)中需要注意一些比較重要的點,假設你現(xiàn)在做的是一個會員開通功能。
1.在你點擊開通的時候,你需要做的肯定是調用你自己的后臺業(yè)務接口生成一個會員訂單,同時調用微信支付獲取支付參數(shù)返回到前端。這樣用戶看到的就是直接拉起支付。
2.當你執(zhí)行支付操作后你的支付回調接口會收到支付結果,這個時候你服務端要主動通知小程序,并且當小程序拉起支付后要定時調用支付查詢接口來主動查詢支付完成支付。做個雙保險比較好。
3.微信支付完成后有個"完成"按鈕,點擊后就會回到wx.requestPayment的success回調里,這里最好也要查詢下訂單狀態(tài)。
4.還有點我還沒怎么做處理,也是個題外話,就是當支付回調時服務器掛了咋整,得想個萬全之策,這個就交給你們解答了。
到此這篇關于利用微信小程序+JAVA實現(xiàn)微信支付的文章就介紹到這了,更多相關微信小程序 JAVA實現(xiàn)微信支付內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot高級配置之臨時屬性、配置文件、日志、多環(huán)境配置詳解
這篇文章主要介紹了SpringBoot高級配置之臨時屬性、配置文件、日志、多環(huán)境配置,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02java實戰(zhàn)技巧之if-else代碼優(yōu)化技巧大全
代碼中如果if-else比較多,閱讀起來比較困難,維護起來也比較困難,很容易出bug,下面這篇文章主要給大家介紹了關于java實戰(zhàn)技巧之if-else代碼優(yōu)化技巧的相關資料,需要的朋友可以參考下2022-02-02Java中bcrypt算法實現(xiàn)密碼加密的方法步驟
我們可以在Spring Boot和SSM中實現(xiàn)密碼加密,使用bcrypt算法可以保障密碼的安全性,并且減少了手動編寫哈希函數(shù)的工作量,本文就來詳細的介紹一下,感興趣的可以了解一下2023-08-08Java中關于isEmpty方法、null以及““的區(qū)別
這篇文章主要介紹了Java中關于isEmpty方法、null以及““的區(qū)別,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08