国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringBoot對(duì)接小程序微信支付

這篇具有很好參考價(jià)值的文章主要介紹了SpringBoot對(duì)接小程序微信支付。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

目錄

前言

一、準(zhǔn)備工作

2.1、企業(yè)微信小程序開(kāi)通

2.1.1、獲取開(kāi)發(fā)者ID

2.1.2、開(kāi)通支付功能

2.1.3、關(guān)聯(lián)商戶號(hào)

2.2、企業(yè)商戶號(hào)的開(kāi)通

2.2.1、獲取商戶號(hào)mch_id

2.2.2、獲取商戶API密鑰mch_key

二、整體流程

三、后端項(xiàng)目搭建

3.1、統(tǒng)一下單

3.2、支付支付回調(diào)

3.3、問(wèn)題排查

3.4、統(tǒng)一下單和訂單查詢(xún)


前言

項(xiàng)目采用SpringBoot

微信支付有兩個(gè)版本:V3 和 V2,本文的接入版本為V2

API V2 和 API V3 的區(qū)別

1、接口請(qǐng)求參數(shù)不同

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2、API V2 調(diào)用流程

在微信v2接口中,只有涉及資金流出或獲取重要信息才會(huì)使用證書(shū),比如退款、企業(yè)付款和下載資金賬單等。

springboot實(shí)現(xiàn)小程序支付,小程序,微信

3、API V3 調(diào)用流程

(1)證書(shū)序列號(hào)

每個(gè)證書(shū)都有一個(gè)由CA頒發(fā)的唯一編號(hào),即證書(shū)序列號(hào)。如果讀取到的序列號(hào)是10進(jìn)制整形需要轉(zhuǎn)換為大寫(xiě)的16進(jìn)制。

(2)平臺(tái)證書(shū)

微信支付平臺(tái)證書(shū)是指由微信支付負(fù)責(zé)申請(qǐng)的,包含微信支付平臺(tái)標(biāo)識(shí)、公鑰信息的證書(shū)。

a、不同的商戶,對(duì)應(yīng)的微信支付平臺(tái)證書(shū)是不一樣的。

b、微信支付APIv3使用微信支付的平臺(tái)私鑰進(jìn)行應(yīng)答簽名,商戶使用平臺(tái)證書(shū)中的公鑰進(jìn)行驗(yàn)簽。

c、微信平臺(tái)證書(shū)會(huì)周期性更換,商戶應(yīng)實(shí)現(xiàn)定期更新平臺(tái)證書(shū)的邏輯實(shí)現(xiàn)平臺(tái)證書(shū)平滑切換,即:定期調(diào)用該接口,間隔時(shí)間小于12 小時(shí),參考獲取平臺(tái)證書(shū)接口。目前已知的微信更換平臺(tái)證書(shū)的場(chǎng)景:

  • 證書(shū)到期后,必須更換。(目前是五年)
  • 證書(shū)到期前,例行更換。(每年一次)

d、舊證書(shū)過(guò)期前10天生成新證書(shū),舊證書(shū)過(guò)期前5天至過(guò)期當(dāng)天,新證書(shū)開(kāi)始逐步放量用于應(yīng)答和回調(diào)的簽名。為了保證更換過(guò)程中不影響API的使用,請(qǐng)求和應(yīng)答的HTTP頭部中包括證書(shū)序列號(hào),以聲明簽名或者加密所用的密鑰對(duì)和證書(shū)。

springboot實(shí)現(xiàn)小程序支付,小程序,微信

一、準(zhǔn)備工作

微信小程序

微信支付

  • 微信小程序賬號(hào):要認(rèn)證、獲取appid、生成secret、開(kāi)通支付、關(guān)聯(lián)商戶號(hào)
  • 微信商戶平臺(tái)賬號(hào):要認(rèn)證、獲取商戶號(hào)mch_id、獲取商戶API密鑰mch_key、APPID授權(quán)、配置支付接口

2.1、企業(yè)微信小程序開(kāi)通

2.1.1、獲取開(kāi)發(fā)者ID

  • 獲取appid:小程序的身份證明
  • 獲取secret:小程序的唯一憑證密鑰

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2.1.2、開(kāi)通支付功能

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2.1.3、關(guān)聯(lián)商戶號(hào)

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2.2、企業(yè)商戶號(hào)的開(kāi)通

2.2.1、獲取商戶號(hào)mch_id

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2.2.2、獲取商戶API密鑰mch_key

springboot實(shí)現(xiàn)小程序支付,小程序,微信

springboot實(shí)現(xiàn)小程序支付,小程序,微信

二、整體流程

springboot實(shí)現(xiàn)小程序支付,小程序,微信

商戶系統(tǒng)與微信支付系統(tǒng)主要交互:

1、JSAPI支付、APP支付、H5支付、Native支付、付款碼支付、小程序支付【微信支付API列表】

2、小程序內(nèi)調(diào)用登錄接口,獲取到用戶的openid【小程序登錄API】

3、商戶server調(diào)用支付統(tǒng)一下單【統(tǒng)一下單API】

4、小程序支付成功回調(diào)notity_url(業(yè)務(wù)邏輯處理)【支付結(jié)果通知】

5、商戶server查詢(xún)支付結(jié)果,如未收到支付通知的情況,商戶后臺(tái)系統(tǒng)可調(diào)用【查詢(xún)訂單API】

三、后端項(xiàng)目搭建

3.1、統(tǒng)一下單

1、導(dǎo)入相關(guān)依賴(lài) pom.yml

        <!-- 微信支付 SDK -->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

2、文件配置微信公眾號(hào)的基礎(chǔ)信息 application.yml

# 微信支付配置 notifyUrl:微信支付異步回調(diào)地址
pay:
  appId: #應(yīng)用id
  apiKey: #商戶私鑰key
  mchId: #商戶id
  appSecret: #小程序密鑰
  notifyUrl: #支付回調(diào)地址

3、設(shè)置配置文件 WxPayConfig.java

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * 微信支付配置
 * @author lf
 * @date 2023/8/30
 */
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "pay")
public class WxPayConfig {

    /**
     * 微信公眾號(hào)appid
     */
    private String appId;

    /**
     * 公眾號(hào)設(shè)置的API v2密鑰
     */
    private String apiKey;

    /**
     * 微信商戶平臺(tái) 商戶id
     */
    private String mchId;

    /**
     *小程序密鑰
     */
    private String appSecret;

    /**
     * 小程序支付異步回調(diào)地址
     */
    private String notifyUrl;

}

4、微信支付預(yù)下單實(shí)體類(lèi) WxChatPay.java

import lombok.Data;
import lombok.experimental.Accessors;

import java.math.BigDecimal;

/**
 * 微信支付預(yù)下單實(shí)體類(lèi)
 */
@Data
@Accessors(chain = true)
public class WeChatPay {

    /**
     * 返回狀態(tài)碼  此字段是通信標(biāo)識(shí),非交易標(biāo)識(shí),交易是否成功需要查看result_code來(lái)判斷
     */
    public String return_code;

    /**
     * 返回信息 當(dāng)return_code為FAIL時(shí)返回信息為錯(cuò)誤原因 ,例如 簽名失敗 參數(shù)格式校驗(yàn)錯(cuò)誤
     */
    private String return_msg;

    /**
     * 公眾賬號(hào)ID 調(diào)用接口提交的公眾賬號(hào)ID
     */
    private String appid;

    /**
     * 商戶號(hào) 調(diào)用接口提交的商戶號(hào)
     */
    private String mch_id;

    /**
     * api密鑰 詳見(jiàn):https://pay.weixin.qq.com/index.php/extend/employee
     */
    private String api_key;

    /**
     * 設(shè)備號(hào)  自定義參數(shù),可以為請(qǐng)求支付的終端設(shè)備號(hào)等
     */
    private String device_info;

    /**
     * 隨機(jī)字符串    5K8264ILTKCH16CQ2502SI8ZNMTM67VS   微信返回的隨機(jī)字符串
     */
    private String nonce_str;

    /**
     * 簽名 微信返回的簽名值,詳見(jiàn)簽名算法:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
     */
    private String sign;

    /**
     * 簽名類(lèi)型
     */
    private String sign_type;


    /**
     * 業(yè)務(wù)結(jié)果 SUCCESS SUCCESS/FAIL
     */
    private String result_code;

    /**
     * 錯(cuò)誤代碼 當(dāng)result_code為FAIL時(shí)返回錯(cuò)誤代碼,詳細(xì)參見(jiàn)下文錯(cuò)誤列表
     */
    private String err_code;

    /**
     * 錯(cuò)誤代碼描述 當(dāng)result_code為FAIL時(shí)返回錯(cuò)誤描述,詳細(xì)參見(jiàn)下文錯(cuò)誤列表
     */
    private String err_code_des;

    /**
     * 交易類(lèi)型 JSAPI JSAPI -JSAPI支付 NATIVE -Native支付 APP -APP支付 說(shuō)明詳見(jiàn);https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String trade_type;

    /**
     * 預(yù)支付交易會(huì)話標(biāo)識(shí) 微信生成的預(yù)支付會(huì)話標(biāo)識(shí),用于后續(xù)接口調(diào)用中使用,該值有效期為2小時(shí)
     */
    private String prepay_id;

    /**
     * 二維碼鏈接     weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00 trade_type=NATIVE時(shí)有返回,此url用于生成支付二維碼,然后提供給用戶進(jìn)行掃碼支付。注意:code_url的值并非固定,使用時(shí)按照URL格式轉(zhuǎn)成二維碼即可
     */
    private String code_url;

    /**
     * 商品描述  商品簡(jiǎn)單描述,該字段請(qǐng)按照規(guī)范傳遞,具體請(qǐng)見(jiàn) https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String body;

    /**
     * 商家訂單號(hào) 商戶系統(tǒng)內(nèi)部訂單號(hào),要求32個(gè)字符內(nèi),只能是數(shù)字、大小寫(xiě)字母_-|* 且在同一個(gè)商戶號(hào)下唯一。詳見(jiàn)商戶訂單號(hào) https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String out_trade_no;

    /**
     * 標(biāo)價(jià)金額 訂單總金額,單位為分,詳見(jiàn)支付金額 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_2
     */
    private String total_fee;

    /**
     * 終端IP 支持IPV4和IPV6兩種格式的IP地址。用戶的客戶端IP
     */
    private String spbill_create_ip;

    /**
     * 通知地址 異步接收微信支付結(jié)果通知的回調(diào)地址,通知url必須為外網(wǎng)可訪問(wèn)的url,不能攜帶參數(shù)。公網(wǎng)域名必須為https,如果是走專(zhuān)線接入,使用專(zhuān)線NAT IP或者私有回調(diào)域名可使用http
     */
    private String notify_url;

    /**
     * 子商戶號(hào) sub_mch_id 非必填(商戶不需要傳入,服務(wù)商模式才需要傳入) 微信支付分配的子商戶號(hào)
     */
    private String sub_mch_id;

    /**
     * 附加數(shù)據(jù),在查詢(xún)API和支付通知中原樣返回,該字段主要用于商戶攜帶訂單的自定義數(shù)據(jù)
     */
    private String attach;

    /**
     * 商戶系統(tǒng)內(nèi)部的退款單號(hào),商戶系統(tǒng)內(nèi)部唯一,只能是數(shù)字、大小寫(xiě)字母_-|*@ ,同一退款單號(hào)多次請(qǐng)求只退一筆。
     */
    private String out_refund_no;

    /**
     * 退款總金額,單位為分,只能為整數(shù),可部分退款。詳見(jiàn)支付金額 https://pay.weixin.qq.com/wiki/doc/api/native_sl.php?chapter=4_2
     */
    private String refund_fee;

    /**
     * 退款原因 若商戶傳入,會(huì)在下發(fā)給用戶的退款消息中體現(xiàn)退款原因 注意:若訂單退款金額≤1元,且屬于部分退款,則不會(huì)在退款消息中體現(xiàn)退款原因
     */
    private String refund_desc;

    /**
     * 交易結(jié)束時(shí)間 訂單失效時(shí)間,格式為yyyyMMddHHmmss,如2009年12月27日9點(diǎn)10分10秒表示為20091227091010。其他詳見(jiàn)時(shí)間規(guī)則 注意:最短失效時(shí)間間隔必須大于5分鐘
     */
    private String time_expire;

    /**
     * 用戶標(biāo)識(shí) trade_type=JSAPI,此參數(shù)必傳,用戶在主商戶appid下的唯一標(biāo)識(shí)。openid和sub_openid可以選傳其中之一,如果選擇傳sub_openid,則必須傳sub_appid。下單前需要調(diào)用【網(wǎng)頁(yè)授權(quán)獲取用戶信息: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 】接口獲取到用戶的Openid。
     */
    private String openid;

    /**
     * 時(shí)間戳
     */
    private String time_stamp;

    /**
     * 會(huì)員類(lèi)型
     */
    private String memberShipType;

}

5、微信支付API地址 WeChatPayUrlContants.java

/**
 * 微信支付API地址
 * @author lf
 * @date 2023/8/30
 */
public class WeChatPayUrlConstants {

    /**
     * 統(tǒng)一下單預(yù)下單接口url
     */
    public static final String Uifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 訂單狀態(tài)查詢(xún)接口URL
     */
    public static final String Orderquery = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     * 訂單申請(qǐng)退款
     */
    public static final String Refund = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    /**
     * 付款碼 支付
     */
    public static final String MicroPay = "https://api.mch.weixin.qq.com/pay/micropay";

    /**
     * 微信網(wǎng)頁(yè)授權(quán) 獲取“code”請(qǐng)求地址
     */
    public static final String GainCodeUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";

    /**
     * 微信網(wǎng)頁(yè)授權(quán) 獲取“code” 回調(diào)地址
     */
    public static final String GainCodeRedirect_uri = "http://i5jmxe.natappfree.cc/boss/WeChatPayMobile/SkipPage.html";

}

?6、預(yù)下單成功之后返回結(jié)果 OrderReturnInfo.java

import lombok.Data;

@Data
public class OrderReturnInfo {

    private String return_code;

    private String return_msg;

    private String result_code;

    private String appid;

    private String mch_id;

    private String nonce_str;

    private String sign;

    private String prepay_id;

    private String trade_type;

}

7、查詢(xún)訂單返回的實(shí)體類(lèi) QueryReturnInfo.java

import lombok.Data;


/**
 * 查詢(xún)訂單返回實(shí)體類(lèi)
 * @author lf
 * @date 2023/9/1
 */
@Data
public class QueryReturnInfo {

    private String return_code;

    private String return_msg;

    private String result_code;

    private String err_code;

    private String err_code_des;

    private String appid;

    private String mch_id;

    private String nonce_str;

    private String sign;

    private String prepay_id;

    private String trade_type;

    private String device_info;

    private String openid;

    private String is_subscribe;

    private String trade_state;

    private String bank_type;

    private int total_fee;

    private int settlement_total_fee;

    private String fee_type;

    private int cash_fee;

    private String cash_fee_type;

    private int coupon_fee;

    private int coupon_count;

    private String coupon_type_$n;

    private String coupon_id_$n;

    private String transaction_id;

    private String out_trade_no;

    private String time_end;

    private String trade_state_desc;

}

8、簽名實(shí)體類(lèi) SignInfo.java

/**
 * 簽名實(shí)體類(lèi)
 * @author lf
 * @date 2023/9/1
 */
@Data
public class SignInfo {

    private String appId;//小程序ID

    private String timeStamp;//時(shí)間戳

    private String nonceStr;//隨機(jī)串

    @XStreamAlias("package")
    private String repay_id;

    private String signType;//簽名方式

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }

    public String getNonceStr() {
        return nonceStr;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public String getRepay_id() {
        return repay_id;
    }

    public void setRepay_id(String repay_id) {
        this.repay_id = repay_id;
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        this.signType = signType;
    }

}

9、Http工具類(lèi) HttpRequest.java

/**
 * Http工具類(lèi)
 * @author lf
 * @date 2023/9/1
 */
public class HttpRequest {

    //連接超時(shí)時(shí)間,默認(rèn)10秒
    private static final int socketTimeout = 10000;

    //傳輸超時(shí)時(shí)間,默認(rèn)30秒
    private static final int connectTimeout = 30000;

    /**
     * post請(qǐng)求
     *
     * @throws IOException
     * @throws ClientProtocolException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     * @throws UnrecoverableKeyException
     */
    public static String sendPost(String url, Object xmlObj) throws ClientProtocolException, IOException, UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException {


        HttpPost httpPost = new HttpPost(url);
        //解決XStream對(duì)出現(xiàn)雙下劃線的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        xStreamForRequestPostData.alias("xml", xmlObj.getClass());
        //將要提交給API的數(shù)據(jù)對(duì)象轉(zhuǎn)換成XML格式數(shù)據(jù)Post給API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);

        //得指明使用UTF-8編碼,否則到API服務(wù)器XML的中文不能被成功識(shí)別
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);

        //設(shè)置請(qǐng)求器的配置
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);

        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "UTF-8");
        return result;
    }

    /**
     * 自定義證書(shū)管理器,信任所有證書(shū)
     *
     * @author pc
     */
    public static class MyX509TrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {

        }

        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {

        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

}

10、微信簽名 SignUtils.java

/**
 * 微信簽名
 * @author lf
 * @date 2023/9/1
 */
public class SignUtils {

    /**
     * 簽名算法
     *
     * @param o 要參與簽名的數(shù)據(jù)對(duì)象
     * @return 簽名
     * @throws IllegalAccessException
     */
    public static String getSign(Object o) throws IllegalAccessException {
        ArrayList<String> list = new ArrayList<String>();
        Class cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            if (f.get(o) != null && f.get(o) != "") {
                String name = f.getName();
                XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
                if (anno != null) {
                    name = anno.value();
                }
                list.add(name + "=" + f.get(o) + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();
        System.out.println("簽名數(shù)據(jù):" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        return result;
    }

    public static String getSign(Map<String, Object> map) {
        ArrayList<String> list = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != "") {
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();
        //Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }
}

11、MD5加密工具類(lèi) MD5.java

import java.security.MessageDigest;

/**
 * MD5加密工具類(lèi)
 * @author lf
 * @date 2023/9/1
 */
public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 轉(zhuǎn)換字節(jié)數(shù)組為16進(jìn)制字串
     *
     * @param b 字節(jié)數(shù)組
     * @return 16進(jìn)制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }

    /**
     * 轉(zhuǎn)換byte到16進(jìn)制
     *
     * @param b 要轉(zhuǎn)換的byte
     * @return 16進(jìn)制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * MD5編碼
     *
     * @param origin 原始字符串
     * @return 經(jīng)過(guò)MD5加密之后的結(jié)果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }
}

12、微信支付配置類(lèi) Configure.java

/**
 * 微信支付配置類(lèi)
 * @author lf
 * @date 2023/9/1
 */
public class Configure {

    /**
     * 商戶支付秘鑰
     */
    private static String key = "";

    public static String getKey() {
        return key;
    }

    public static void setKey(String key) {
        Configure.key = key;
    }

}

13、Controller層,PayparameterVO可以不需要,我這里是因?yàn)闃I(yè)務(wù)需要,處理業(yè)務(wù)邏輯

    /**
     * 小程序支付下單接口
     * @return 返回結(jié)果
     */
    @ApiOperation("小程序支付功能")
    @PostMapping("/pay")
    public AjaxResult wxPay(@RequestBody PayParameterVO payParameterVO){
        Map payHistory = wxPayInfoService.insertPayRecord(payParameterVO);
        return success("success",payHistory);
    }


    /**
     * 查詢(xún)訂單
     */
    @ApiOperation("訂單查詢(xún)")
    @PostMapping("/wx/query")
    public AjaxResult orderQuery(@RequestParam("out_trade_no") String out_trade_no) {
       Map query = wxPayInfoService.orderQuery(out_trade_no);
       return success("success", query);
    }

14、業(yè)務(wù)接口層 WxPayInfoService.java

import com.ruoyi.ai.doamin.PayParameterVO;

import java.util.Map;

/**
 * 微信小程序支付-業(yè)務(wù)接口層
 * @author lf
 * @date 2023/8/31
 */
public interface WxPayInfoService {

    /**
     * 插入訂單記錄
     */
    Map insertPayRecord(PayParameterVO payParameterVO);

    /**
     * 查詢(xún)訂單
     * @param out_trade_no 訂單號(hào)
     * @return 返回結(jié)果
     */
    Map orderQuery(String out_trade_no);

}

15、業(yè)務(wù)接口實(shí)現(xiàn)層?WxPayInfoServiceImpl.java

import cn.hutool.core.util.ObjectUtil;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.ai.doamin.PayParameterVO;
import com.ruoyi.ai.service.WxPayInfoService;
import com.ruoyi.common.config.WxPayConfig;
import com.ruoyi.common.constant.WeChatPayUrlConstants;
import com.ruoyi.common.core.domain.pay.WeChatPay;
import com.ruoyi.common.utils.pay.HttpRequest;
import com.ruoyi.common.utils.pay.SignUtils;
import com.ruoyi.common.utils.pay.entity.OrderReturnInfo;
import com.ruoyi.common.utils.pay.entity.QueryReturnInfo;
import com.ruoyi.common.utils.pay.entity.SignInfo;
import com.thoughtworks.xstream.XStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 微信小程序支付-業(yè)務(wù)接口實(shí)現(xiàn)層
 * @author lf
 * @date 2023/8/31
 */
@Service
@Slf4j
public class WxPayInfoServiceImpl implements WxPayInfoService {

    @Resource
    private WxPayConfig payProperties;

    private static final DecimalFormat df = new DecimalFormat("#");

    /**
     * 插入訂單記錄
     * @param payParameterVO 用戶ID 會(huì)員套餐ID
     * @return 返回結(jié)果
     */
    @Override
    @Transactional
    public Map insertPayRecord(PayParameterVO payParameterVO) {
            //接收返回的參數(shù)
            Map<String, Object> map = new HashMap<>();
            String title = "koko測(cè)試點(diǎn)數(shù)";
            //金額 * 100 以分為單位
            BigDecimal fee = BigDecimal.valueOf(1);
            BigDecimal RMB = new BigDecimal(100);
            BigDecimal totalFee = fee.multiply(RMB);

            try {
                WeChatPay weChatPay = new WeChatPay();
                weChatPay.setAppid(payProperties.getAppId());
                weChatPay.setMch_id(payProperties.getMchId());
                weChatPay.setNonce_str(getRandomStringByLength(32));
                weChatPay.setBody(title);
                weChatPay.setOut_trade_no(getRandomStringByLength(32));
                weChatPay.setTotal_fee( df.format(Double.parseDouble(String.valueOf(totalFee))));
                weChatPay.setSpbill_create_ip("127.0.0.1");
                weChatPay.setNotify_url(payProperties.getNotifyUrl());
                weChatPay.setTrade_type("JSAPI");
                //這里直接使用當(dāng)前用戶的openid
                weChatPay.setOpenid("oOKq*******xj8o");
                weChatPay.setSign_type("MD5");
                //生成簽名
                String sign = SignUtils.getSign(weChatPay);
                weChatPay.setSign(sign);

                String result = HttpRequest.sendPost(WeChatPayUrlConstants.Uifiedorder, weChatPay);
                System.out.println(result);

                //將返回結(jié)果從xml格式轉(zhuǎn)換為map格式
                Map<String, String> wxResultMap = WXPayUtil.xmlToMap(result);
                if (ObjectUtil.isNotEmpty(wxResultMap.get("return_code")) && wxResultMap.get("return_code").equals("SUCCESS")){
                    if (wxResultMap.get("result_code").equals("FAIL")){
                        map.put("msg", "統(tǒng)一下單失敗");
                        map.put("status",500);
                        map.put("data", wxResultMap.get("err_code_des"));
                        return map;
                    }
                }
                XStream xStream = new XStream();
                xStream.alias("xml", OrderReturnInfo.class);

                OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);
                // 二次簽名
                if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                    SignInfo signInfo = new SignInfo();
                    signInfo.setAppId(payProperties.getAppId());
                    long time = System.currentTimeMillis() / 1000;
                    signInfo.setTimeStamp(String.valueOf(time));
                    signInfo.setNonceStr(WXPayUtil.generateNonceStr());
                    signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
                    signInfo.setSignType("MD5");
                    //生成簽名
                    String sign1 = SignUtils.getSign(signInfo);
                    Map<String, String> payInfo = new HashMap<>();
                    payInfo.put("timeStamp", signInfo.getTimeStamp());
                    payInfo.put("nonceStr", signInfo.getNonceStr());
                    payInfo.put("package", signInfo.getRepay_id());
                    payInfo.put("signType", signInfo.getSignType());
                    payInfo.put("paySign", sign1);
                    map.put("status", 200);
                    map.put("msg", "統(tǒng)一下單成功!");
                    map.put("data", payInfo);

                    //預(yù)下單成功,處理業(yè)務(wù)邏輯
                    //****************************//

                    // 業(yè)務(wù)邏輯結(jié)束 回傳給小程序端喚起支付
                    return map;
                }
                map.put("status", 500);
                map.put("msg", "統(tǒng)一下單失敗!");
                map.put("data", null);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    /**
     * 查詢(xún)訂單
     * @param out_trade_no 訂單號(hào)
     * @return 返回結(jié)果
     */
    @Override
    public Map orderQuery(String out_trade_no){

        Map<String, Object> map = new HashMap<>();

        try {
            WeChatPay weChatPay = new WeChatPay();
            weChatPay.setAppid(payProperties.getAppId());
            weChatPay.setMch_id(payProperties.getMchId());
            weChatPay.setNonce_str(WXPayUtil.generateNonceStr());
            weChatPay.setOut_trade_no(out_trade_no);
            //order.setSign_type("MD5");
            //生成簽名
            String sign = SignUtils.getSign(weChatPay);
            weChatPay.setSign(sign);

            String result = HttpRequest.sendPost(WXPayConstants.ORDERQUERY_URL, weChatPay);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.alias("xml", QueryReturnInfo.class);

            QueryReturnInfo returnInfo = (QueryReturnInfo) xStream.fromXML(result);
            map.put("status", 500);
            map.put("msg", "統(tǒng)一下單失敗!");
            map.put("data", returnInfo);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 獲取一定長(zhǎng)度的隨機(jī)字符串
     *
     * @param length 指定字符串長(zhǎng)度
     * @return 一定長(zhǎng)度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

}

3.2、支付支付回調(diào)

    /**
     * 微信小程序支付成功回調(diào)
     * @param request 請(qǐng)求
     * @param response 響應(yīng)
     * @return 返回結(jié)果
     * @throws Exception 異常處理
     */
    @RequestMapping("/weixin/callback")
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("接口已被調(diào)用");
        ServletInputStream inputStream = request.getInputStream();
        String notifyXml = StreamUtils.inputStream2String(inputStream, "utf-8");
        System.out.println(notifyXml);

        // 解析返回結(jié)果
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyXml);
        // 判斷支付是否成功
        if ("SUCCESS".equals(notifyMap.get("result_code"))) {

            //支付成功時(shí)候,處理業(yè)務(wù)邏輯

            System.out.println("支付成功");
            System.out.println("<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ");
            
            /**
             * 注意
             * 因?yàn)槲⑿呕卣{(diào)會(huì)有八次之多,所以當(dāng)?shù)谝淮位卣{(diào)成功了,那么我們就不再執(zhí)行邏輯了
             * return返回的結(jié)果一定是這種格式,當(dāng)result_code返回的結(jié)果是SUCCESS時(shí),則不進(jìn)行調(diào)用了
             * 如果不返回下面的格式,業(yè)務(wù)邏輯會(huì)出現(xiàn)回調(diào)多次的情況,我就遇到過(guò)這種情況。
             */
            return "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
        }

        // 創(chuàng)建響應(yīng)對(duì)象:微信接收到校驗(yàn)失敗的結(jié)果后,會(huì)反復(fù)的調(diào)用當(dāng)前回調(diào)函數(shù)
        Map<String, String> returnMap = new HashMap<>();
        returnMap.put("return_code", "FAIL");
        returnMap.put("return_msg", "");
        String returnXml = WXPayUtil.mapToXml(returnMap);
        response.setContentType("text/xml");
        System.out.println("校驗(yàn)失敗");
        return returnXml;
    }

注:如果能正常走預(yù)支付的接口,而沒(méi)有處理回調(diào)接口的業(yè)務(wù)邏輯。

1、返回的xml格式是否正確,V2會(huì)比對(duì)xml返回的數(shù)據(jù)格式是否一致

springboot實(shí)現(xiàn)小程序支付,小程序,微信2、回調(diào)的接口是否允許外網(wǎng)訪問(wèn),并且接口不能攜帶任何參數(shù),如果框架存在token驗(yàn)證時(shí),則需要關(guān)閉支付回調(diào)的token驗(yàn)證。

springboot實(shí)現(xiàn)小程序支付,小程序,微信3、如果以上都沒(méi)問(wèn)題,卻還是沒(méi)走回調(diào),可以看看這篇文檔【接收不到回調(diào)排查指引】

3.3、問(wèn)題排查

1、在與前端聯(lián)調(diào)時(shí),如果出現(xiàn)如下問(wèn)題,看看預(yù)下單的packeage參數(shù)是否正確,必須嚴(yán)格按照如下格式:

package: "prepay_id=wx*********0000";

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2、在與前端聯(lián)調(diào)時(shí),如果出現(xiàn)如下問(wèn)題,看看簽名的工具類(lèi)是否能夠驗(yàn)證通過(guò)【簽名校驗(yàn)】

springboot實(shí)現(xiàn)小程序支付,小程序,微信

3.4、統(tǒng)一下單和訂單查詢(xún)

1、統(tǒng)一下單

springboot實(shí)現(xiàn)小程序支付,小程序,微信

2、訂單查詢(xún)-支付成功

springboot實(shí)現(xiàn)小程序支付,小程序,微信

3、訂單查詢(xún)-支付失敗

springboot實(shí)現(xiàn)小程序支付,小程序,微信

springboot實(shí)現(xiàn)小程序支付,小程序,微信

?4、預(yù)下單成功并且支付成功

springboot實(shí)現(xiàn)小程序支付,小程序,微信

springboot實(shí)現(xiàn)小程序支付,小程序,微信文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-765708.html

到了這里,關(guān)于SpringBoot對(duì)接小程序微信支付的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • springboot整合IJPay實(shí)現(xiàn)微信支付-V3---微信小程序

    springboot整合IJPay實(shí)現(xiàn)微信支付-V3---微信小程序

    微信支付適用于許多場(chǎng)合,如小程序、網(wǎng)頁(yè)支付、但微信支付相對(duì)于其他支付方式略顯麻煩,我們使用IJpay框架進(jìn)行整合 JPay 讓支付觸手可及, 封裝了微信支付、支付寶支付、銀聯(lián)支付常用的支付方式以及各種常用的接口。不依賴(lài)任何第三方 mvc 框架,僅僅作為工具使用簡(jiǎn)單

    2024年02月02日
    瀏覽(58)
  • uniapp+java/springboot實(shí)現(xiàn)微信小程序APIV3支付功能

    uniapp+java/springboot實(shí)現(xiàn)微信小程序APIV3支付功能

    微信小程序的支付跟H5的支付和APP支付流程不一樣,本次只描述下小程序支付流程。 1.微信小程序賬號(hào) 文檔:小程序申請(qǐng) 小程序支付需要先認(rèn)證,如果你有已認(rèn)證的公眾號(hào),也可以通過(guò)公眾號(hào)免費(fèi)注冊(cè)認(rèn)證小程序。 一般300元,我是認(rèn)證的政府的免費(fèi)。 然后登錄小程序,設(shè)置

    2023年04月19日
    瀏覽(31)
  • SpringBoot對(duì)接支付寶完成掃碼支付

    SpringBoot對(duì)接支付寶完成掃碼支付

    需求:系統(tǒng)A對(duì)接支付寶,實(shí)現(xiàn)支持用戶掃碼支付 對(duì)接的API文檔:https://open.alipay.com/api 可選的支付方式有: 掃碼付:出示付款碼或者用戶掃碼付款 APP支付:在APP中喚起支付寶 手機(jī)網(wǎng)站支付:在移動(dòng)端網(wǎng)頁(yè)中喚起支付寶 App 或支付寶網(wǎng)頁(yè) 電腦網(wǎng)站支付:在PC端喚起支付寶App或

    2024年02月03日
    瀏覽(28)
  • SpringBoot + 微信支付(小程序)案例

    SpringBoot + 微信支付(小程序)案例

    https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal https://mp.weixin.qq.com/wxamp/basicprofile/index?token=1160814128lang=zh_CN 進(jìn)入后找到最下方的:設(shè)置-微信認(rèn)證 需要 300RMB https://pay.weixin.qq.com/index.php/extend/merchant_appid/mapay_platform/accoun

    2024年02月07日
    瀏覽(13)
  • springboot 小程序微信支付

    springboot 小程序微信支付

    看效果 ? 參考文章:SpringBoot + 小程序 整合微信支付和退款 V2版 - 碼農(nóng)教程 本文僅作記錄 參考文章:?UniApp + SpringBoot 實(shí)現(xiàn)微信支付和退款 - 奔跑的磚頭 - 博客園 Springboot整合支付寶支付參考:?UniApp + SpringBoot 實(shí)現(xiàn)支付寶支付和退款 - 奔跑的磚頭 - 博客園 微信支付商戶號(hào)接入微

    2024年02月05日
    瀏覽(11)
  • SpringBoot整合微信小程序支付V3(支付、退款)

    微信支付開(kāi)發(fā)前,需要先獲取商家信息,包括商戶號(hào)、AppId、證書(shū)和密鑰。 獲取商戶號(hào) 微信商戶平臺(tái) 申請(qǐng)成為商戶 = 提交資料 = 簽署協(xié)議 = 獲取商戶號(hào) 獲取AppID 微信公眾平臺(tái) 注冊(cè)服務(wù)號(hào) = 服務(wù)號(hào)認(rèn)證 = 獲取APPID = 綁定商戶號(hào) 申請(qǐng)商戶證書(shū) 登錄商戶平臺(tái) = 選擇 賬戶中心 = 安

    2024年02月08日
    瀏覽(50)
  • SpringBoot 整合微信小程序微信支付V3 jsapi (支付、退款)

    SpringBoot 整合微信小程序微信支付V3 jsapi (支付、退款)

    最近的一個(gè)微信小程序項(xiàng)目里有用到微信支付,網(wǎng)上找的資料都是特別亂,看起來(lái)特別懵,結(jié)合了好多文章的內(nèi)容,終于做了出來(lái),可能我的這個(gè)博文看起來(lái)也是特別亂,但是是可以直接C走簡(jiǎn)單改一改就可以用的。(支付成功回調(diào),和退款回調(diào)因?yàn)樽蛱靹傇诎⒗锷暾?qǐng)的域名還

    2024年04月25日
    瀏覽(25)
  • SpringBoot實(shí)現(xiàn)微信支付

    在目前的互聯(lián)網(wǎng)上,我們已經(jīng)經(jīng)常使用微信支付進(jìn)行各種支付操作。微信支付作為一種移動(dòng)支付的新時(shí)代產(chǎn)品,新穎的功能和穩(wěn)定的操作流程使得它在用戶中深得信任。對(duì)于一些企業(yè)或者個(gè)人,如何實(shí)現(xiàn)和使用微信支付成了一個(gè)不可避免的問(wèn)題。為此,本文將介紹如何使用

    2024年02月13日
    瀏覽(12)
  • 07_SpringBoot 對(duì)接支付寶完成掃碼支付,完整流程梳理!

    07_SpringBoot 對(duì)接支付寶完成掃碼支付,完整流程梳理!

    需求:系統(tǒng)A對(duì)接支付寶,實(shí)現(xiàn)支持用戶掃碼支付 對(duì)接的API文檔: https://open.alipay.com/api 可選的支付方式有: 掃碼付 :出示付款碼或者用戶掃碼付款 APP支付 :在APP中喚起支付寶 手機(jī)網(wǎng)站支付 :在移動(dòng)端網(wǎng)頁(yè)中喚起支付寶 App 或支付寶網(wǎng)頁(yè) 電腦網(wǎng)站支付 :在PC端喚起支付寶

    2024年04月16日
    瀏覽(39)
  • 保姆級(jí)教程:SpringBoot 對(duì)接支付寶完成掃碼支付,完整流程梳理!

    保姆級(jí)教程:SpringBoot 對(duì)接支付寶完成掃碼支付,完整流程梳理!

    1、支付方式選擇 2、交互流程 3、1. 對(duì)接準(zhǔn)備 2.加密解密 + 簽名驗(yàn)簽 3.沙箱環(huán)境 4、內(nèi)網(wǎng)穿透 5、二維碼 6、下單 7、異步通知回調(diào) 8、查詢(xún)支付結(jié)果 9、退款 10、通用版SDK 需求:系統(tǒng)A對(duì)接支付寶,實(shí)現(xiàn)支持用戶掃碼支付 對(duì)接的API文檔: https://open.alipay.com/api 可選的支付方式有:

    2024年04月11日
    瀏覽(26)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包