微信支付APIV3

準(zhǔn)備工作

  <dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.2.1</version>
  </dependency>
  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
  </dependency>

1.統(tǒng)一下單

private Map<String, String> wxPay(String openId, BigDecimal consume, Orders orders, String notifyUrl) throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //WxPayConfig.certPath 為證書存放路徑匿级,絕對路徑陶缺。eg:/root/xx/xx/xx.pem
        PrivateKey privateKey = WxUtils.create().getPrivateKey(WxPayConfig.certPath);
        CloseableHttpClient client = WxUtils.create().client();
        String param = ModelOrder.WxCreateOrder.create()
                .setAmount(new ModelOrder.WxCreateOrder.Amount(consume.intValue(), "CNY"))
                .setMchid(WxPayConfig.mchId)
                .setDescription(orders.getContent())
                .setNotify_url(WxPayConfig.notifyUrl + notifyUrl)
                .setPayer(new ModelOrder.WxCreateOrder.Payer(openId))
                .setOut_trade_no(orders.getCode())
                .setAppid(WxPayConfig.appId).build();
        logger.debug(param);
        Map<String, String> headers = new HashMap<>(3);
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
       //參考另一篇文章中的HttpUtils
        String ret = HttpUtils.create().headers(headers).client(client).post(WxUtils.CREATE_ORDER, param);
        ModelOrder.WxCreateOrderResult result = new Gson().fromJson(ret, new TypeToken<ModelOrder.WxCreateOrderResult>() {
        }.getType());
        String nonceStr = WxUtils.create().nonceStr(32).toUpperCase();
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String packAge = "prepay_id=" + result.getPrepay_id();
        String signType = "RSA";
        String message = WxPayConfig.appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packAge + "\n";
        logger.debug(message);
        String sign = WxUtils.create().paySign(message, privateKey);
        Map<String, String> data = new HashMap<>(6);
        data.put("appId", WxPayConfig.appId);
        data.put("timeStamp", timeStamp);
        data.put("nonceStr", nonceStr);
        data.put("package", packAge);
        data.put("signType", signType);
        data.put("paySign", sign);
        return data;
    }

2.微信支付相關(guān)處理類 WxUtils.java

public class WxUtils {
    //生成微信支付nonce使用的字符
    private static final String BASE_CHAR = "qwertyuiopasdfghjklzxcvbnm1234567890";
    //以下static final 均為微信支付所需url,自行查看文檔
    public static final String CODE_SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
    public static final String SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN";
    public static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    public static final String CREATE_CODE = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN";
    public static final String CREATE_ORDER = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    public static final String REFUND = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    public static final String QUERY_ORDER = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{order}?mchid={mchid}";
    private WxUtils() {
    }

    public static WxUtils create() {
        return new WxUtils();
    }

    public String paySign(String message, PrivateKey key) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(key);
        sign.update(message.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public String nonceStr(int len) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        int length = BASE_CHAR.length();
        for (int i = 0; i < len; i++) {
            int number = random.nextInt(length);
            sb.append(BASE_CHAR.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 獲取私鑰。
     *
     * @param filename 私鑰文件路徑  (required)
     * @return 私鑰對象
     */
    public PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("當(dāng)前Java環(huán)境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("無效的密鑰格式");
        }
    }

    public CloseableHttpClient client() throws IOException {
        //WxPayConfig.mchId 商戶ID茄靠,WxPayConfig.v3Secret apiv3 secret
        //WxPayConfig.mchSerial 商戶序列號
        PrivateKey privateKey = getPrivateKey(WxPayConfig.certPath);
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(WxPayConfig.mchId, new PrivateKeySigner(WxPayConfig.mchSerial, privateKey)),
                WxPayConfig.v3Secret.getBytes(StandardCharsets.UTF_8));
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(WxPayConfig.mchId, WxPayConfig.mchSerial, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        return builder.build();
    }
}

3.支付回調(diào)

public NotifyResult invest(@RequestBody NotifyBody body) throws IOException, GeneralSecurityException {
        logger.info("余額充值支付回調(diào):" + body.toString());
        if (StringUtils.equals("TRANSACTION.SUCCESS", body.getEvent_type())) {
            NotifyBody.Resource resource = body.getResource();
            String ret = new AesUtil(WxPayConfig.v3Secret.getBytes(StandardCharsets.UTF_8)).decryptToString(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8), resource.getNonce().getBytes(StandardCharsets.UTF_8), resource.getCiphertext());
            NotifyResource result = new Gson().fromJson(ret, NotifyResource.class);
            if (StringUtils.equals(result.getTrade_state(), "SUCCESS")) {
                //支付回調(diào) 數(shù)據(jù)解析成功后璧榄,相關(guān)處理
            }
            logger.debug(ret);
        }
        return NotifyResult.create().success();
    }

4.接收回調(diào)參數(shù)類 NotifyBody.java

public class NotifyBody {
    private String id;
    private String create_time;
    private String event_type;
    private String resource_type;
    private String summary;
    private Resource resource;

    public static class Resource {
        private String algorithm;
        private String ciphertext;
        private String associated_data;
        private String original_type;
        private String nonce;

        @Override
        public String toString() {
            return "Resource{" +
                    "algorithm='" + algorithm + '\'' +
                    ", ciphertext='" + ciphertext + '\'' +
                    ", associated_data='" + associated_data + '\'' +
                    ", original_type='" + original_type + '\'' +
                    ", nonce='" + nonce + '\'' +
                    '}';
        }

        public String getAlgorithm() {
            return algorithm;
        }

        public void setAlgorithm(String algorithm) {
            this.algorithm = algorithm;
        }

        public String getCiphertext() {
            return ciphertext;
        }

        public void setCiphertext(String ciphertext) {
            this.ciphertext = ciphertext;
        }

        public String getAssociated_data() {
            return associated_data;
        }

        public void setAssociated_data(String associated_data) {
            this.associated_data = associated_data;
        }

        public String getOriginal_type() {
            return original_type;
        }

        public void setOriginal_type(String original_type) {
            this.original_type = original_type;
        }

        public String getNonce() {
            return nonce;
        }

        public void setNonce(String nonce) {
            this.nonce = nonce;
        }
    }

    @Override
    public String toString() {
        return "NotifyBody{" +
                "id='" + id + '\'' +
                ", create_time='" + create_time + '\'' +
                ", event_type='" + event_type + '\'' +
                ", resource_type='" + resource_type + '\'' +
                ", summary='" + summary + '\'' +
                ", resource=" + resource +
                '}';
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCreate_time() {
        return create_time;
    }

    public void setCreate_time(String create_time) {
        this.create_time = create_time;
    }

    public String getEvent_type() {
        return event_type;
    }

    public void setEvent_type(String event_type) {
        this.event_type = event_type;
    }

    public String getResource_type() {
        return resource_type;
    }

    public void setResource_type(String resource_type) {
        this.resource_type = resource_type;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public Resource getResource() {
        return resource;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }
}

5.向微信返回結(jié)果類NotifyResult.java

public class NotifyResult {
    private String code;
    private String message;

    public NotifyResult() {
    }
    public static NotifyResult create(){
        return new NotifyResult();
    }
    public NotifyResult success(){
        this.code = "SUCCESS";
        this.message = "成功";
        return this;
    }
    public NotifyResult fail(){
        this.code = "FAIL";
        this.message = "失敗";
        return this;
    }
    public NotifyResult fail(String message){
        this.code = "FAIL";
        this.message = message;
        return this;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

6.至此 微信支付集成完畢

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末特漩,一起剝皮案震驚了整個濱河市吧雹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涂身,老刑警劉巖雄卷,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛤售,居然都是意外死亡丁鹉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門悴能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揣钦,“玉大人,你說我怎么就攤上這事漠酿》氚迹” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵炒嘲,是天一觀的道長宇姚。 經(jīng)常有香客問我,道長夫凸,這世上最難降的妖魔是什么浑劳? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮夭拌,結(jié)果婚禮上魔熏,老公的妹妹穿的比我還像新娘。我一直安慰自己鸽扁,他們只是感情好蒜绽,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桶现,像睡著了一般滓窍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巩那,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音此蜈,去河邊找鬼即横。 笑死,一個胖子當(dāng)著我的面吹牛裆赵,可吹牛的內(nèi)容都是我干的东囚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼战授,長吁一口氣:“原來是場噩夢啊……” “哼页藻!你這毒婦竟也來了桨嫁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤份帐,失蹤者是張志新(化名)和其女友劉穎璃吧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體废境,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡畜挨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了噩凹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巴元。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驮宴,靈堂內(nèi)的尸體忽然破棺而出逮刨,到底是詐尸還是另有隱情,我是刑警寧澤堵泽,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布修己,位于F島的核電站,受9級特大地震影響落恼,放射性物質(zhì)發(fā)生泄漏箩退。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一佳谦、第九天 我趴在偏房一處隱蔽的房頂上張望戴涝。 院中可真熱鬧,春花似錦钻蔑、人聲如沸啥刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽可帽。三九已至,卻和暖如春窗怒,著一層夾襖步出監(jiān)牢的瞬間映跟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工扬虚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留努隙,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓辜昵,卻偏偏與公主長得像荸镊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容