利用微信小程序+JAVA實(shí)現(xiàn)微信支付的全過程
本文主要講的是小程序?qū)崿F(xiàn)微信支付功能,后臺(tái)采用JAVA。
一.準(zhǔn)備工作
1.小程序
2.微信商戶號(hào)
1.商戶號(hào)申請(qǐng)
這里對(duì)小程序的申請(qǐng)不做贅述。
如果沒有微信商戶號(hào)的同學(xué),點(diǎn)擊該鏈接https://pay.weixin.qq.com/,按照下屬步驟進(jìn)行商戶號(hào)申請(qǐng)。
掃碼之后點(diǎn)擊"成為商家",這里主要有個(gè)體工商戶和企業(yè),按照事實(shí)填寫,然后按照步驟填寫就行了。
主要需要營(yíng)業(yè)執(zhí)照,法人信息,公賬信息等。
2.微信商戶號(hào)關(guān)聯(lián)小程序
點(diǎn)擊"產(chǎn)品中心"的"開發(fā)配置",點(diǎn)擊"新增授權(quán)申請(qǐng)單"。
輸入你的小程序appid,點(diǎn)擊下一步。
然后到小程序后臺(tái)>微信支付>商戶號(hào)管理里,會(huì)出現(xiàn)一個(gè)申請(qǐng)單,點(diǎn)擊“查看”。
點(diǎn)擊確認(rèn)綁定,這樣你的商戶號(hào)就與小程序進(jìn)行綁定了。
二.代碼編寫
1.小程序
小程序這塊主要是調(diào)用一下后臺(tái)接口獲取參數(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) { // 支付成功的回調(diào) }, 'fail': function (res) { console.log(JSON.stringify(res)); wx.showToast({title: '支付失敗', icon: 'none',duration: 2000,mask: true}) } }) },
這里的payInfo就是從后臺(tái)接口獲取的支付參數(shù),通過wx.requestPayment就可以拉起微信支付了。具體的參數(shù)信息在下面會(huì)進(jìn)行講解。
2.服務(wù)端(JAVA)
服務(wù)端這邊主要是三個(gè)接口:
1.提交支付訂單
這個(gè)主要是為了獲取提交支付訂單,獲取前端拉起支付的參數(shù)。
2.微信支付回調(diào)
當(dāng)你小程序拉起支付并且成功支付后,會(huì)將支付結(jié)果回調(diào)到這個(gè)接口
3.支付訂單查詢
你也可以主動(dòng)通過訂單號(hào)查詢支付訂單狀態(tài)
下面是我的代碼,包含了我的業(yè)務(wù)代碼,大家將就著看吧
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 * 微信支付回調(diào) * */ @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 * 支付回調(diào)處理 * 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 * 更新會(huì)員時(shí)間 * */ 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 * 將支付結(jié)果下發(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 * 構(gòu)建支付請(qǐng)求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 * 構(gòu)造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 * 構(gòu)建支付請(qǐng)求參數(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()); // 回調(diào)接口地址 prepayRequest.setDescription("微信支付"); prepayRequest.setOutTradeNo(orderId); // 訂單號(hào) prepayRequest.setAttach("member"); // 訂單類型(回調(diào)時(shí)可根據(jù)這個(gè)數(shù)據(jù)辨別訂單類型或其他) //根據(jù)token拿到openid,指定該預(yù)支付訂單的支付者身份 Payer payer = new Payer(); payer.setOpenid(WeixinUserBo.getOpenId(userId)); prepayRequest.setPayer(payer); return prepayRequest; } /** * * @author Rick chou * @date 2024/7/16 9:53 * 解析支付結(jié)果 * */ 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 * 解析回調(diào)結(jié)果 * */ 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) // 若未設(shè)置signType,默認(rèn)值為 WECHATPAY2-SHA256-RSA2048 .body(data) .build(); return requestParam; } /** * * @author Rick chou * @date 2024/7/16 9:47 * 調(diào)起支付 * */ 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 * 支付回調(diào) * */ 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; //商戶號(hào) private String mchId; //證書序列號(hào) private String mchSerialNo; //小程序秘鑰 private String appSecret; //api秘鑰 private String apiKey; //回調(diào)接口地址 private String notifyUrl; //證書地址 private String keyPath; }
上述的PayInfoConfig中的參數(shù)第二、三、五、七個(gè)參數(shù)去商戶號(hào)后臺(tái)獲取。
三.補(bǔ)充說明
在實(shí)際的支付開發(fā)中需要注意一些比較重要的點(diǎn),假設(shè)你現(xiàn)在做的是一個(gè)會(huì)員開通功能。
1.在你點(diǎn)擊開通的時(shí)候,你需要做的肯定是調(diào)用你自己的后臺(tái)業(yè)務(wù)接口生成一個(gè)會(huì)員訂單,同時(shí)調(diào)用微信支付獲取支付參數(shù)返回到前端。這樣用戶看到的就是直接拉起支付。
2.當(dāng)你執(zhí)行支付操作后你的支付回調(diào)接口會(huì)收到支付結(jié)果,這個(gè)時(shí)候你服務(wù)端要主動(dòng)通知小程序,并且當(dāng)小程序拉起支付后要定時(shí)調(diào)用支付查詢接口來主動(dòng)查詢支付完成支付。做個(gè)雙保險(xiǎn)比較好。
3.微信支付完成后有個(gè)"完成"按鈕,點(diǎn)擊后就會(huì)回到wx.requestPayment的success回調(diào)里,這里最好也要查詢下訂單狀態(tài)。
4.還有點(diǎn)我還沒怎么做處理,也是個(gè)題外話,就是當(dāng)支付回調(diào)時(shí)服務(wù)器掛了咋整,得想個(gè)萬全之策,這個(gè)就交給你們解答了。
到此這篇關(guān)于利用微信小程序+JAVA實(shí)現(xiàn)微信支付的文章就介紹到這了,更多相關(guān)微信小程序 JAVA實(shí)現(xiàn)微信支付內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計(jì)模式的策略模式簡(jiǎn)析
這篇文章主要介紹了Java設(shè)計(jì)模式的策略模式簡(jiǎn)析,策略模式中定義了一系列的算法族,算法族指的是類似于一系列的行為、策略,策略模式將一系列的行為封裝成類,既可以說是將每一種相類似的行為都封裝成一個(gè)類,也有可能存在特殊的不進(jìn)行封裝的行為,需要的朋友可以參考下2023-12-12SpringBoot高級(jí)配置之臨時(shí)屬性、配置文件、日志、多環(huán)境配置詳解
這篇文章主要介紹了SpringBoot高級(jí)配置之臨時(shí)屬性、配置文件、日志、多環(huán)境配置,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02使用spring stream發(fā)送消息代碼實(shí)例
這篇文章主要介紹了使用spring stream發(fā)送消息代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05java實(shí)戰(zhàn)技巧之if-else代碼優(yōu)化技巧大全
代碼中如果if-else比較多,閱讀起來比較困難,維護(hù)起來也比較困難,很容易出bug,下面這篇文章主要給大家介紹了關(guān)于java實(shí)戰(zhàn)技巧之if-else代碼優(yōu)化技巧的相關(guān)資料,需要的朋友可以參考下2022-02-02使用HttpClient調(diào)用接口的實(shí)例講解
下面小編就為大家?guī)硪黄褂肏ttpClient調(diào)用接口的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10idea雙擊圖標(biāo)打不開,無反應(yīng)的解決
這篇文章主要介紹了idea雙擊圖標(biāo)打不開,無反應(yīng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java中bcrypt算法實(shí)現(xiàn)密碼加密的方法步驟
我們可以在Spring Boot和SSM中實(shí)現(xiàn)密碼加密,使用bcrypt算法可以保障密碼的安全性,并且減少了手動(dòng)編寫哈希函數(shù)的工作量,本文就來詳細(xì)的介紹一下,感興趣的可以了解一下2023-08-08Java中關(guān)于isEmpty方法、null以及““的區(qū)別
這篇文章主要介紹了Java中關(guān)于isEmpty方法、null以及““的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08