微信境外支付(附PHP例子,JAVA工具類)

官方API:https://wechatpay-api.gitbook.io/wechatpay-api-v3/

推薦文章:https://blog.csdn.net/taoweifeng199311/article/details/100031159

API文檔:

鏈接:https://pan.baidu.com/s/1FrLkL0qj5Y9JuK0VXDpHnQ 
提取碼:vc1q 

PHP 例子:

鏈接:https://pan.baidu.com/s/1lZjsoUjDp6fGOzQqvV2JsQ 
提取碼:askc 

遇到問題:

  • java.security.InvalidKeyException: Illegal key size

解決方法:

產生錯誤原因:爲了數據代碼在傳輸過程中的安全,很多時候我們都會將要傳輸的數據進行加密,然後等對方拿到後再解密使用。我們在使用AES加解密的時候,在遇到128位密鑰加解密的時候,沒有進行什麼特殊處理;然而,在使用256位密鑰加解密的時候,如果不進行特殊處理的話,往往會出現這個異常java.security.InvalidKeyException: Illegal key size。

爲什麼會產生這樣的錯誤?

我們做Java開發,或是Android開發,都會先在電腦上安裝JDK(Java Development Kit) 並配置環境變量,JDK也就是 Java 語言的軟件開發工具包,JDK中包含有JRE(Java Runtime Environment,即:Java運行環境),JRE中包括Java虛擬機(Java Virtual Machine)、Java核心類庫和支持文件,而我們今天要說的主角就在Java的核心類庫中。在Java的核心類庫中有一個JCE(Java Cryptography Extension),JCE是一組包,它們提供用於加密、密鑰生成和協商以及 Message Authentication Code(MAC)算法的框架和實現,所以這個是實現加密解密的重要類庫。

在我們安裝的JRE目錄下有這樣一個文件夾:%JAVE_HOME%\jre\lib\security(%JAVE_HOME%是自己電腦的Java路徑,一版默認是:C:\Program Files\Java,具體看自己當時安裝JDK和JRE時選擇的路徑是什麼),其中包含有兩個.jar文件:“local_policy.jar ”和“US_export_policy.jar”,也就是我們平時說的jar包,再通俗一點說就是Java中包含的類庫(Sun公司的程序大牛封裝的類庫,供使用Java開發的程序員使用),這兩個jar包就是我們JCE中的核心類庫了。JRE中自帶的“local_policy.jar ”和“US_export_policy.jar”是支持128位密鑰的加密算法,而當我們要使用256位密鑰算法的時候,已經超出它的範圍,無法支持,所以纔會報:“java.security.InvalidKeyException: Illegal key size or default parameters”的異常。那麼我們怎麼解決呢?

如何解決?

解決方案:去官方下載JCE無限制權限策略文件。

jdk 5: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jce_policy-1.5.0-oth-JPR

jdk6: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

JDK7的下載地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8的下載地址: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 

下載後解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件。

如果安裝了JDK,還要將兩個jar文件也放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件。

具體下載、解壓、替換步驟:

1.下載:根據自己使用的jdk版本進入對應的鏈接下載(我這裏是jdk8)

2.解壓:

3.替換:(原來的2個jar包你可以備份下)

4.如果不行就重啓下軟件,刷新下maven

 
————————————————
原文鏈接:https://blog.csdn.net/dling8/article/details/84061948

  • $key0參數格式錯誤$

參數錯誤會導致次錯誤,本人遇到的是因爲openid錯誤和商業行業編碼(merchant_category_code)錯誤

商業行業編碼:

Merchant Category Codes  Merchant Category  商戶類目
5309 Duty-free shops 免稅店
5311 Department stores 百貨商店
5310 Discount shops 折扣商店
5531 Auto and home supply outlets 汽車商店、家庭用品商店
5611 Men’s and boys’ clothing and accessory shops 男子和男童服裝及用品商店
5621 Women’s ready-to-wear shops 婦女成衣商店
5631 Women’s accessory and speciality shops 女性用品商店
5641 Children’s and infants’ wear shops 嬰兒、兒童服裝店
5651 Family clothing shops 家庭服裝商店
5655 Sports and riding apparel shops 運動服飾商店
5661 Shoe shops 鞋店
5681 Furriers and fur shops 皮貨店
5691 Men’s and women’s clothing shops 成人成衣店
5699 Miscellaneous apparel and accessory shops 各類服裝及飾物店
5712 Furniture, home furnishings and equipment shops and manufacturers, except appliances 傢俱、家庭擺品、家用設備零售商
5722 Household appliance shops 家用電器商店
5944 Jewellery, watch, clock and silverware shops 珠寶、鐘錶、銀器店
5948 Luggage and leather goods shops 箱包、皮具店
5094 Precious stones and metals, watches and jewellery 寶石和金屬、手錶和珠寶
5331 Variety stores 各類雜貨店、便利店
5411 Groceries and supermarkets 大型倉儲式超級市場
5200 Home supply warehouse outlets 大型倉儲式家庭用品賣場
5697 Tailors, seamstresses, mending and alterations 裁縫、修補、改衣店
5698 Wig and toupee shops 假髮商店
5713 Floor covering services 地板商店
5714 Drapery, window covering and upholstery shops 幃帳、窗簾、室內裝潢商店
5715 Alcoholic beverage wholesalers 酒精飲料
5718 Fireplaces, fireplace screens and accessories shops 壁爐、壁爐防護網及配件商店
5719 Miscellaneous home furnishing speciality shops 各種家庭裝飾專營店
5732 Electronics shops 電子設備商店
5733 Music shops - musical instruments, pianos and sheet music 音樂商店-樂器、鋼琴、樂譜
5734 Computer software outlets 計算機軟件商店
5735 Record shops 音像製品商店
5921 Package shops - beer, wine and liquor 瓶裝酒零售店
5931 Used merchandise and second-hand shops 舊商品店、二手商品店
5932 Antique shop - sale, repair and restoration 古玩店-出售、維修及還原
5937 Antique reproduction shops 古玩複製店
5940 Bicycle shops - sales and service 自行車商店
5941 Sporting goods shops 體育用品店
5942 Bookshops 書店
5943 Stationery, office and school supply shops 文具用品商店、各類辦公用品商店
5945 Hobby, toy and game shops 玩具、遊戲店
5946 Camera and photographic supply shops 照相器材商店
5947 Gift, card, novelty and souvenir shops 禮品、卡片、裝飾品、紀念品商店
5949 Sewing, needlework, fabric and piece goods shops 紡織品及針織品零售
5950 Glassware and crystal shops 玻璃器皿和水晶飾品店
5970 Artist supply and craft shops 工藝美術商店
5971 Art dealers and galleries 藝術商和畫廊
5972 Stamp and coin shops 郵票和紀念幣商店
5973 Religious goods and shops 宗教品商店
5977 Cosmetic shops 化妝品商店
5978 Typewriter outlets - sales, service and rentals 打字機商店-銷售、服務和出租
5992 Florists 花店
5993 Cigar shops and stands 香菸、雪茄專賣店
5994 Newsagents and news-stands 報亭、報攤
5995 Pet shops, pet food and supplies 寵物商店、寵物食品及用品
5997 Electric razor shops - sales and service 電動剃刀商店-銷售和服務
5999 Miscellaneous and speciality retail outlets 其他專門零售店
5044 Office, photographic, photocopy and microfilm equipment 辦公、影印及微縮攝影器材
5045 Computers, computer peripheral equipment - not elsewhere classified 計算機、計算機外圍設備
5131 Piece goods, notions and other dry goods 布料、縫紉用品和其他紡織品
5192 Books, periodicals and newspapers 書、期刊和報紙
5231 Glass, paint and wallpaper shops 玻璃、油漆塗料、牆紙零售
5251 Hardware shops 五金商店
5261 Lawn and garden supplies outlets, including nurseries 草坪、花園用品商店
5422 Freezer and locker meat provisioners 各類肉類零售商
5441 Candy, nut and confectionery shops 糖果及堅果商店
5451 Dairies 乳製品店、冷飲店
5462 Bakeries 麪包房、糕點商店
5499 Miscellaneous food shops - convenience and speciality retail outlets 各類食品店及專門食品零售店
5532 Automotive tyre outlets 汽車輪胎經銷商
5533 Automotive parts and accessories outlets 汽車零配件商店
5571 Motorcycle shops and dealers 摩托車商店和經銷商
7011 Lodging - hotels, motels and resorts 住宿服務(旅館、酒店、汽車旅館、度假村等)
7012 Time-sharing villa or holiday home 分時使用的別墅或度假用房
7032 Sporting and recreational camps 運動和娛樂露營地
7033 Trailer parks and camp-sites 活動房車場及露營場所
5811 Caterers 包辦伙食,宴會承包商
5812 Eating places and restaurants 就餐場所和餐館
5814 Fast food restaurants 快餐店
4011 Railroads 鐵路運輸
4111 Local and suburban commuter passenger transportation, including ferries 本市和市郊通勤旅客運輸(包括輪渡)
4112 Passenger railways 鐵路客運
4119 Ambulance services 救護車服務
4121 Taxi-cabs and limousines 出租車服務
4131 Bus lines 公路客運
4214 Motor freight carriers and trucking - local and long distance, moving and storage companies and local delivery 貨物搬運和託運—當地和長途,移動和存儲公司,以及當地遞送服務
4215 Courier services - air and ground and freight forwarders 快遞服務(空運、地面運輸或海運)
4411 Steamships and cruise lines 輪船及巡遊航線服務
4457 Boat rentals and leasing 出租船隻
4468 Marinas, marine service and supplies 船舶、海運服務提供商
4511 Airlines and air carriers 航空公司
4784 Tolls and bridge fees 路橋通行費
7210 Laundry, cleaning and garment services 洗衣店
7211 Laundry services - family and commercial 洗熨服務(自助洗衣服務)
7216 Dry cleaners 乾洗店
7217 Carpet and upholstery cleaning 室內清潔服務(地毯、沙發、傢俱表面的清潔服務)
7221 Photographic studios 攝影工作室
7230 Beauty and barber shops 美容理髮店
7251 Shoe repair shops, shoe shine parlours and hat cleaning shops 修鞋店、擦鞋店、帽子清洗店
7261 Funeral services and crematoriums 殯葬服務
7273 Dating and escort services 婚姻介紹及陪同服務
7311 Advertising services 廣告服務
7333 Commercial photography, art and graphics 商業攝影、工藝、繪圖服務
7338 Quick copy, reproduction and blueprinting services 複印及繪圖服務
7339 Stenographic and secretarial support services 速記、祕書服務(包括各類辦公服務)
7342 Exterminating and disinfecting services 滅蟲及消毒服務
7349 Cleaning, maintenance and janitorial services 清潔、保養及門衛服務
7361 Employment agencies and temporary help services 職業中介、臨時工
7372 Computer programming, data processing and integrated systems design services 計算機編程、數據處理和系統集成設計服務
7375 Information retrieval services 信息檢索服務
7393 Detective agencies, protective agencies and security services, including armoured cars and guard dogs 偵探、保安、安全服務
7394 Equipment, tool, furniture and appliance rentals and leasing 設備、工具、傢俱和電器出租
7395 Photofinishing laboratories and photo developing 照相洗印服務
7512 Automobile rentals 汽車出租
7513 Truck and utility trailer rentals 卡車及拖車出租
7519 Motor home and recreational vehicle rentals 房車和娛樂車輛出租
7523 Parking lots and garages 停車場
7531 Automotive body repair shops 車體維修店
7534 Tyre retreading and repair shops 輪胎翻新、維修店
7535 Automotive paint shops 汽車噴漆店
7538 Automotive service shops (non-dealer) 汽車服務商店(非經銷商)
7542 Car washes 洗車
7549 Towing services 拖車服務
7622 Electronics repair shops 電器設備維修
7623 Air conditioning and refrigeration repair shops 空調、製冷設備維修
7629 Electrical and small appliance repair shops 電器設備、小家電維修
7631 Watch, clock and jewellery repair shops 手錶、鐘錶和首飾維修店
7641 Furniture reupholstery, repair and refinishing 傢俱維修、翻新
7692 Welding services 焊接維修服務
7699 Miscellaneous repair shops and related services 各類維修店及相關服務
7295 Babysitting and housekeeping services 家政服務
7296 Clothing rentals - costumes, uniforms and formal wear 出租衣物-服裝、制服和正式場合服裝
4900 Utilities - electric, gas, water and sanitary 公共事業(電力、煤氣、自來水、清潔服務)
5541 Service stations (with or without ancillary services) 加油站、服務站
5542 Automated fuel dispensers 自助加油站
4582 Airports, flying fields and airport terminals 機場服務
5511 Car and truck dealers (new and used) sales, services, repairs, parts and leasing 汽車貨車經銷商-新舊車的銷售、服務、維修、零件及出租
5521 Car and truck dealers (used only) sales, service, repairs, parts and leasing 汽車貨車經銷商-專門從事舊車的銷售、服務、維修、零件及出租
9400 Embassy and consulate fees 使領館收費
9402 Postal Services - Government Only 國家郵政服務
4733 Ticketing 票務
4722 Travel agencies and tour operators 旅行社
7829 Motion picture and video tape production and distribution 電影和錄像創作、發行
7832 Motion picture theatres 電影院
7841 Video tape rentals 音像製品出租商店
7911 Dance halls, studios and schools 歌舞廳
7922 Theatrical producers (except motion pictures) and ticket agencies 戲劇製片(不含電影)、演出和票務
7932 Billiard and pool establishments 檯球、撞球場所
7933 Bowling alleys 保齡球館
7941 Commercial sports, professional sports clubs, athletic fields and sports promoters 商業運動
7991 Tourist attractions and exhibits 景點、展覽
7992 Public golf courses 公開高爾夫球賽
7993 Video amusement game supplies 電子遊戲等
7994 Video game arcades and establishments 大型遊戲機和遊戲場所
7996 Amusement parks, circuses, carnivals and fortune tellers 遊樂園、馬戲團、嘉年華、占卜
7997 Membership clubs (sports, recreation, athletic), country clubs and private golf courses 會員俱樂部(體育、娛樂、運動等)、鄉村俱樂部以及私人高爾夫課程班
7998 Aquariums, seaquariums and dolphinariums 水族館、海洋館和海豚館
5813 Drinking places (alcoholic beverages) - bars, taverns, night-clubs, cocktail lounges and discothèques 飲酒場所(酒吧、酒館、夜總會、雞尾酒大廳、迪斯科舞廳)
7297 Massage parlours 按摩店
7298 Health and beauty spas 保健及美容SPA
4812 Telecommunication equipment and telephone sales 電信設備和銷售
4814 Telecommunication services 電信服務,包括本地和長途電話、信用卡電話、磁卡電話和傳真
4815 Monthly summary telephone charges 月結電話服務
4816 Computer network information services 計算機網絡/信息服務
4899 Cable and other pay television services 有線和其他付費電視服務
7280 private hospital 私人醫院
8011 Doctors and physicians - not elsewhere classified 其他醫療衛生活動
8021 Dentists and orthodontists 牙科醫生
8031 Osteopaths 正骨醫生
8041 Chiropractors 按摩醫生
8042 Optometrists and ophthalmologists 眼科醫生
8043 Opticians, optical goods and eyeglasses 光學產品、眼鏡店
8049 Podiatrists and chiropodists 手足病醫生
8050 Nursing and personal care facilities 護理和照料服務
8062 Hospitals 公立醫院
8071 Medical and dental laboratories 醫學及牙科實驗室
8099 Medical services and health practitioners - not elsewhere classified 其他醫療保健服務
5912 Drug stores and pharmacies 藥店、藥房
5975 Hearing aids - sales, service and supplies 助聽器-銷售、服務和用品
5976 Orthopaedic goods and prosthetic devices 假肢店(整形外科用品、輔助設備)
5047 Dental laboratory medical ophthalmic hospital equipment and supplies 牙科/實驗室/醫療/眼科醫院器材和用品
8211 Elementary and secondary schools 中小學校(公立)
8220 Colleges, universities, professional schools and junior colleges 普通高校(公立)
8241 Correspondence schools 函授學校(成人教育)
8244 Business and secretarial schools 商業和文祕學校(中等專業學校)
8249 Trade and vocational schools 貿易和職業學校(職業技能培訓)
8299 Schools and educational services - not elsewhere classified 其他學校和教育服務
8351 Child care services 兒童保育服務(含學前教育)

 

  • 工具類

AesUtil.java 微信通知解密用

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;


public class AesUtil {

    private       Logger logger          = LoggerFactory.getLogger(AesUtil.class);
    static final  int    KEY_LENGTH_BYTE = 32;
    static final  int    TAG_LENGTH_BIT  = 128;
    private final byte[] aesKey;

    public AesUtil(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("無效的ApiV3Key,長度必須爲32個字節");
        }
        this.aesKey = key;
    }

    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
                throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            logger.error("境外支付解密", e);
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            logger.error("境外支付解密", e);
            throw new IllegalArgumentException(e);
        }
    }
}

WxPayApi.java 簽名、解籤用

package com.yogovi.core.service.weixin.v3;

import com.alibaba.fastjson.JSON;
import com.yogovi.core.entity.weixin.v3.NotifyV3;
import com.yogovi.core.service.impl.weixin.v3.AesUtil;
import com.yogovi.core.service.weixin.WXPayUtil;
import com.yogovi.core.entity.weixin.v3.notify.NotifyData;
import okhttp3.HttpUrl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;


/**
 * 境外微信支付V3
 *
 * @author hangyin
 * @since 2019-09-26
 */
public class WxPayApi {

    private static Logger logger = WXPayUtil.getLogger();

    /**
     * 簽名生成
     *
     * @param method       請求類型GET、POST
     * @param url          請求url
     * @param body         GET請求時body傳"",POST請求時body爲請求參數的json串
     * @param merchantId   商戶號
     * @param certSerialNo API證書序列號
     * @param keyPath      API證書路徑
     * @param nonceStr     隨機字符串
     * @param timeStamp    當前秒數
     * @return
     * @throws Exception
     */
    public static String getToken(String method, String url, String body, String merchantId, String certSerialNo,
                String keyPath, String nonceStr, String timeStamp) throws Exception {
        String signStr;
        HttpUrl httpurl = HttpUrl.parse(url);
        if (StringUtils.isEmpty(body)) {
            body = "";
        }
        String message = buildMessage(method, httpurl, timeStamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"), keyPath);
        signStr = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + merchantId
                    + "\",nonce_str=\"" + nonceStr
                    + "\",timestamp=\"" + timeStamp
                    + "\",serial_no=\"" + certSerialNo
                    + "\",signature=\"" + signature + "\"";
        logger.info("Authorization Token:" + signStr);
        return signStr;
    }

    public static String buildMessage(String method, HttpUrl url, String timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                    + canonicalUrl + "\n"
                    + timestamp + "\n"
                    + nonceStr + "\n"
                    + body + "\n";
    }

    public static String sign(byte[] message, String keyPath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(keyPath));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

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

    /**
     * 調起微信支付簽名
     *
     * @param appId
     * @param timestamp
     * @param nonceStr
     * @param packages
     * @param prvKeyPath 私鑰證書路徑
     * @return
     * @throws Exception
     */
    public static String paySign(String appId, String timestamp, String nonceStr, String packages, String prvKeyPath)
                throws Exception {
        String message = appId + "\n"
                    + timestamp + "\n"
                    + nonceStr + "\n"
                    + packages + "\n";
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(prvKeyPath));
        sign.update(message.getBytes("utf-8"));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 微信境外支付異步通知參數解密方式
     *
     * @param apiKey   Api密鑰
     * @param notifyV3 通知對象
     * @return
     * @throws Exception
     */
    public static NotifyData decrypt(String apiKey, NotifyV3 notifyV3)
                throws Exception {
        AesUtil aesUtil = new AesUtil(apiKey.getBytes(StandardCharsets.UTF_8));
        String data = notifyV3.getResource().getAssociated_data();
        String nonce = notifyV3.getResource().getNonce();
        String ciphertext = notifyV3.getResource().getCiphertext();
        String res = aesUtil.decryptToString(
                    data.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        return JSON.parseObject(res, NotifyData.class);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章