微信境外支付(附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 商户类目
5309Duty-free shops免税店
5311Department stores百货商店
5310Discount shops折扣商店
5531Auto and home supply outlets汽车商店、家庭用品商店
5611Men’s and boys’ clothing and accessory shops男子和男童服装及用品商店
5621Women’s ready-to-wear shops妇女成衣商店
5631Women’s accessory and speciality shops女性用品商店
5641Children’s and infants’ wear shops婴儿、儿童服装店
5651Family clothing shops家庭服装商店
5655Sports and riding apparel shops运动服饰商店
5661Shoe shops鞋店
5681Furriers and fur shops皮货店
5691Men’s and women’s clothing shops成人成衣店
5699Miscellaneous apparel and accessory shops各类服装及饰物店
5712Furniture, home furnishings and equipment shops and manufacturers, except appliances家具、家庭摆品、家用设备零售商
5722Household appliance shops家用电器商店
5944Jewellery, watch, clock and silverware shops珠宝、钟表、银器店
5948Luggage and leather goods shops箱包、皮具店
5094Precious stones and metals, watches and jewellery宝石和金属、手表和珠宝
5331Variety stores各类杂货店、便利店
5411Groceries and supermarkets大型仓储式超级市场
5200Home supply warehouse outlets大型仓储式家庭用品卖场
5697Tailors, seamstresses, mending and alterations裁缝、修补、改衣店
5698Wig and toupee shops假发商店
5713Floor covering services地板商店
5714Drapery, window covering and upholstery shops帏帐、窗帘、室内装潢商店
5715Alcoholic beverage wholesalers酒精饮料
5718Fireplaces, fireplace screens and accessories shops壁炉、壁炉防护网及配件商店
5719Miscellaneous home furnishing speciality shops各种家庭装饰专营店
5732Electronics shops电子设备商店
5733Music shops - musical instruments, pianos and sheet music音乐商店-乐器、钢琴、乐谱
5734Computer software outlets计算机软件商店
5735Record shops音像制品商店
5921Package shops - beer, wine and liquor瓶装酒零售店
5931Used merchandise and second-hand shops旧商品店、二手商品店
5932Antique shop - sale, repair and restoration古玩店-出售、维修及还原
5937Antique reproduction shops古玩复制店
5940Bicycle shops - sales and service自行车商店
5941Sporting goods shops体育用品店
5942Bookshops书店
5943Stationery, office and school supply shops文具用品商店、各类办公用品商店
5945Hobby, toy and game shops玩具、游戏店
5946Camera and photographic supply shops照相器材商店
5947Gift, card, novelty and souvenir shops礼品、卡片、装饰品、纪念品商店
5949Sewing, needlework, fabric and piece goods shops纺织品及针织品零售
5950Glassware and crystal shops玻璃器皿和水晶饰品店
5970Artist supply and craft shops工艺美术商店
5971Art dealers and galleries艺术商和画廊
5972Stamp and coin shops邮票和纪念币商店
5973Religious goods and shops宗教品商店
5977Cosmetic shops化妆品商店
5978Typewriter outlets - sales, service and rentals打字机商店-销售、服务和出租
5992Florists花店
5993Cigar shops and stands香烟、雪茄专卖店
5994Newsagents and news-stands报亭、报摊
5995Pet shops, pet food and supplies宠物商店、宠物食品及用品
5997Electric razor shops - sales and service电动剃刀商店-销售和服务
5999Miscellaneous and speciality retail outlets其他专门零售店
5044Office, photographic, photocopy and microfilm equipment办公、影印及微缩摄影器材
5045Computers, computer peripheral equipment - not elsewhere classified计算机、计算机外围设备
5131Piece goods, notions and other dry goods布料、缝纫用品和其他纺织品
5192Books, periodicals and newspapers书、期刊和报纸
5231Glass, paint and wallpaper shops玻璃、油漆涂料、墙纸零售
5251Hardware shops五金商店
5261Lawn and garden supplies outlets, including nurseries草坪、花园用品商店
5422Freezer and locker meat provisioners各类肉类零售商
5441Candy, nut and confectionery shops糖果及坚果商店
5451Dairies乳制品店、冷饮店
5462Bakeries面包房、糕点商店
5499Miscellaneous food shops - convenience and speciality retail outlets各类食品店及专门食品零售店
5532Automotive tyre outlets汽车轮胎经销商
5533Automotive parts and accessories outlets汽车零配件商店
5571Motorcycle shops and dealers摩托车商店和经销商
7011Lodging - hotels, motels and resorts住宿服务(旅馆、酒店、汽车旅馆、度假村等)
7012Time-sharing villa or holiday home分时使用的别墅或度假用房
7032Sporting and recreational camps运动和娱乐露营地
7033Trailer parks and camp-sites活动房车场及露营场所
5811Caterers包办伙食,宴会承包商
5812Eating places and restaurants就餐场所和餐馆
5814Fast food restaurants快餐店
4011Railroads铁路运输
4111Local and suburban commuter passenger transportation, including ferries本市和市郊通勤旅客运输(包括轮渡)
4112Passenger railways铁路客运
4119Ambulance services救护车服务
4121Taxi-cabs and limousines出租车服务
4131Bus lines公路客运
4214Motor freight carriers and trucking - local and long distance, moving and storage companies and local delivery货物搬运和托运—当地和长途,移动和存储公司,以及当地递送服务
4215Courier services - air and ground and freight forwarders快递服务(空运、地面运输或海运)
4411Steamships and cruise lines轮船及巡游航线服务
4457Boat rentals and leasing出租船只
4468Marinas, marine service and supplies船舶、海运服务提供商
4511Airlines and air carriers航空公司
4784Tolls and bridge fees路桥通行费
7210Laundry, cleaning and garment services洗衣店
7211Laundry services - family and commercial洗熨服务(自助洗衣服务)
7216Dry cleaners干洗店
7217Carpet and upholstery cleaning室内清洁服务(地毯、沙发、家具表面的清洁服务)
7221Photographic studios摄影工作室
7230Beauty and barber shops美容理发店
7251Shoe repair shops, shoe shine parlours and hat cleaning shops修鞋店、擦鞋店、帽子清洗店
7261Funeral services and crematoriums殡葬服务
7273Dating and escort services婚姻介绍及陪同服务
7311Advertising services广告服务
7333Commercial photography, art and graphics商业摄影、工艺、绘图服务
7338Quick copy, reproduction and blueprinting services复印及绘图服务
7339Stenographic and secretarial support services速记、秘书服务(包括各类办公服务)
7342Exterminating and disinfecting services灭虫及消毒服务
7349Cleaning, maintenance and janitorial services清洁、保养及门卫服务
7361Employment agencies and temporary help services职业中介、临时工
7372Computer programming, data processing and integrated systems design services计算机编程、数据处理和系统集成设计服务
7375Information retrieval services信息检索服务
7393Detective agencies, protective agencies and security services, including armoured cars and guard dogs侦探、保安、安全服务
7394Equipment, tool, furniture and appliance rentals and leasing设备、工具、家具和电器出租
7395Photofinishing laboratories and photo developing照相洗印服务
7512Automobile rentals汽车出租
7513Truck and utility trailer rentals卡车及拖车出租
7519Motor home and recreational vehicle rentals房车和娱乐车辆出租
7523Parking lots and garages停车场
7531Automotive body repair shops车体维修店
7534Tyre retreading and repair shops轮胎翻新、维修店
7535Automotive paint shops汽车喷漆店
7538Automotive service shops (non-dealer)汽车服务商店(非经销商)
7542Car washes洗车
7549Towing services拖车服务
7622Electronics repair shops电器设备维修
7623Air conditioning and refrigeration repair shops空调、制冷设备维修
7629Electrical and small appliance repair shops电器设备、小家电维修
7631Watch, clock and jewellery repair shops手表、钟表和首饰维修店
7641Furniture reupholstery, repair and refinishing家具维修、翻新
7692Welding services焊接维修服务
7699Miscellaneous repair shops and related services各类维修店及相关服务
7295Babysitting and housekeeping services家政服务
7296Clothing rentals - costumes, uniforms and formal wear出租衣物-服装、制服和正式场合服装
4900Utilities - electric, gas, water and sanitary公共事业(电力、煤气、自来水、清洁服务)
5541Service stations (with or without ancillary services)加油站、服务站
5542Automated fuel dispensers自助加油站
4582Airports, flying fields and airport terminals机场服务
5511Car and truck dealers (new and used) sales, services, repairs, parts and leasing汽车货车经销商-新旧车的销售、服务、维修、零件及出租
5521Car and truck dealers (used only) sales, service, repairs, parts and leasing汽车货车经销商-专门从事旧车的销售、服务、维修、零件及出租
9400Embassy and consulate fees使领馆收费
9402Postal Services - Government Only国家邮政服务
4733Ticketing票务
4722Travel agencies and tour operators旅行社
7829Motion picture and video tape production and distribution电影和录像创作、发行
7832Motion picture theatres电影院
7841Video tape rentals音像制品出租商店
7911Dance halls, studios and schools歌舞厅
7922Theatrical producers (except motion pictures) and ticket agencies戏剧制片(不含电影)、演出和票务
7932Billiard and pool establishments台球、撞球场所
7933Bowling alleys保龄球馆
7941Commercial sports, professional sports clubs, athletic fields and sports promoters商业运动
7991Tourist attractions and exhibits景点、展览
7992Public golf courses公开高尔夫球赛
7993Video amusement game supplies电子游戏等
7994Video game arcades and establishments大型游戏机和游戏场所
7996Amusement parks, circuses, carnivals and fortune tellers游乐园、马戏团、嘉年华、占卜
7997Membership clubs (sports, recreation, athletic), country clubs and private golf courses会员俱乐部(体育、娱乐、运动等)、乡村俱乐部以及私人高尔夫课程班
7998Aquariums, seaquariums and dolphinariums水族馆、海洋馆和海豚馆
5813Drinking places (alcoholic beverages) - bars, taverns, night-clubs, cocktail lounges and discothèques饮酒场所(酒吧、酒馆、夜总会、鸡尾酒大厅、迪斯科舞厅)
7297Massage parlours按摩店
7298Health and beauty spas保健及美容SPA
4812Telecommunication equipment and telephone sales电信设备和销售
4814Telecommunication services电信服务,包括本地和长途电话、信用卡电话、磁卡电话和传真
4815Monthly summary telephone charges月结电话服务
4816Computer network information services计算机网络/信息服务
4899Cable and other pay television services有线和其他付费电视服务
7280private hospital私人医院
8011Doctors and physicians - not elsewhere classified其他医疗卫生活动
8021Dentists and orthodontists牙科医生
8031Osteopaths正骨医生
8041Chiropractors按摩医生
8042Optometrists and ophthalmologists眼科医生
8043Opticians, optical goods and eyeglasses光学产品、眼镜店
8049Podiatrists and chiropodists手足病医生
8050Nursing and personal care facilities护理和照料服务
8062Hospitals公立医院
8071Medical and dental laboratories医学及牙科实验室
8099Medical services and health practitioners - not elsewhere classified其他医疗保健服务
5912Drug stores and pharmacies药店、药房
5975Hearing aids - sales, service and supplies助听器-销售、服务和用品
5976Orthopaedic goods and prosthetic devices假肢店(整形外科用品、辅助设备)
5047Dental laboratory medical ophthalmic hospital equipment and supplies牙科/实验室/医疗/眼科医院器材和用品
8211Elementary and secondary schools中小学校(公立)
8220Colleges, universities, professional schools and junior colleges普通高校(公立)
8241Correspondence schools函授学校(成人教育)
8244Business and secretarial schools商业和文秘学校(中等专业学校)
8249Trade and vocational schools贸易和职业学校(职业技能培训)
8299Schools and educational services - not elsewhere classified其他学校和教育服务
8351Child 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);
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值