jfinal微信小程序支付

package cn.ktc.jkf.utils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.util.TextUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.jfinal.core.JFinal;
import com.jfinal.json.FastJson;
import com.jfinal.json.JFinalJson;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.Kv;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;

import okhttp3.HttpUrl;

/**
 * 微信
 * @author As40098
 *
 */
public class WeChatUtils {
    // 小程序ID
    private static String appid = "wx00000000000000";
    
    // 小程序密钥
    private static String appSecret = "1680000000000000000000000000000000";
    
    // 直连商户号
    private static String mchId = "160000000000";
    
    // 商户平台设置的密钥
    private static String key = "3bonQ85555555555555555555555555555";
    
    // 证书序列号
    private static String serialNo = "57F90A5D1F62000000000000000000000";
    
    // 私钥文件地址
    private static String privateKeyFileName = "/assets/1622811294_20220331_cert/apiclient_key.pem";
    
    /**
     * 获取权限信息
     * @param code
     * @return String {openid=og5I06q7LiV82tu-m9daktwlKKKg, session_key=ox6rjOTkIIAT+Ujkji14ng==}
     */
    public static String getAuthInfo(String code) {
        Map<String, String> data = new HashMap<String, String>();
        data.put("appid", appid);
        data.put("secret", appSecret);
        data.put("grant_type", "authorization_code");
        data.put("js_code", code);
        String result = HttpKit.get("https://api.weixin.qq.com/sns/jscode2session", data);
        
        return result;
    }
    
    /**
     * 获取手机号信息
     * @param encryptedData
     * @param iv
     * @param sessionKey
     * @return Kv{"phoneNumber":"13145985931","purePhoneNumber":"13145985931","countryCode":"86","watermark":{"timestamp":1648601482,"appid":"wx5f7c2b02ba91715e"}}
     */
    public static String getPhoneNumber(String encryptedData, String iv, String sessionKey) {
        try {
            byte[] keyByte = Base64.getDecoder().decode(sessionKey);
            byte[] dataByte = Base64.getDecoder().decode(encryptedData);
            byte[] ivByte = Base64.getDecoder().decode(iv);
            
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            Key sKeySpec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
            
            params.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, params);
            
            byte[] result = cipher.doFinal(dataByte);
            
            return new String (result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 获取支付签名信息
     * @return
     */
    public static Map<String, Object> getPaySign(String prepayId) {
        String signType = "RSA";
        Long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = WeChatUtils.getRandomString(20);
        String packageStr = "prepay_id="+prepayId;
        
        String paySign;
        try {
            String message = appid + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n";
            paySign = WeChatUtils.sign(message.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("appid", appid);
        params.put("timeStamp", timeStamp.toString());
        params.put("nonceStr", nonceStr);
        params.put("package", packageStr);
        params.put("signType", signType);
        params.put("paySign", paySign);
        
        return params;
    }
    
    /***
     * 统一下单(生成预付单)
     * @param ipAddr
     * @param openid
     * @param orderSn
     * @param price
     * @return 
     */
    public static String unifiedOrder(String openid, String orderSn, int price, String notifyUrl) {
        try {
            Kv amount = Kv.by("total", price).set("currency", "CNY");
            Kv payer = Kv.by("openid", openid);
            
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("appid", appid);
            params.put("mchid", mchId);
            params.put("description", "云屏汇-会员充值(开发测试单)");// 商品描述 
            params.put("out_trade_no", orderSn);// 商户订单号
            params.put("notify_url", notifyUrl);// 通知地址
            params.put("amount", amount);
            params.put("payer", payer);
            
            String postData = JFinalJson.getJson().toJson(params);
            String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
            Map<String, String> header = WeChatUtils.getAuthorization(payUrl, "POST", postData);
            
            String jsonString = HttpKit.post(payUrl, postData, header);
            Kv data = FastJson.getJson().parse(jsonString, Kv.class);
            if(data != null) {
                String prepayId = data.getStr("prepay_id");
                if(prepayId != null) {
                    return prepayId;
                }
            }
            System.out.println("支付返回结果:"+jsonString);
        } catch (Exception e) {      
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 商户订单号查询
     * @param outTradeNo
     */
    public static String getOrder(String outTradeNo) {
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s", outTradeNo, mchId);
        Map<String, String> header = WeChatUtils.getAuthorization(url, "GET", "");
        try {
            String result = HttpKit.get(url, null, header);
            System.out.println("查询支付结果:"+result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 检查订单是否已经支付
     * @param outTradeNo
     * @return
     */
    public static boolean checkOrderIsPay(String outTradeNo) {
        // 获取订单信息
        String orderStr = WeChatUtils.getOrder(outTradeNo);
        if(!TextUtils.isEmpty(orderStr)) {
            Kv data = FastJson.getJson().parse(orderStr, Kv.class);
            if(data != null && data.getStr("trade_state").equals("SUCCESS")) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 异步回调加密参数进行解密
     * @param body
     * @return
     */
    public static String decodeNotify(String body) {
        Kv data = FastJson.getJson().parse(body, Kv.class);
        Kv resource = FastJson.getJson().parse(data.getStr("resource"), Kv.class);
        String associatedData = resource.getStr("associated_data");
        String nonceStr = resource.getStr("nonce");
        String cipherText = resource.getStr("ciphertext");
        
        AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
        try {
            return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonceStr.getBytes(StandardCharsets.UTF_8), cipherText);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 获取header部分的权限信息
     * @param payUrl
     * @param method
     * @param body
     * @return
     */
    private static Map<String, String> getAuthorization(String payUrl, String method, String body) {
        String nonceStr = WeChatUtils.getRandomString(16);
        long timestamp = System.currentTimeMillis() / 1000;
        
        HttpUrl httpurl = HttpUrl.parse(payUrl);
        String canonicalUrl = httpurl.encodedPath();
        if (httpurl.encodedQuery() != null) {
          canonicalUrl += "?" + httpurl.encodedQuery();
        }
        String message = method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
        String signature = WeChatUtils.sign(message.getBytes());
        String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + mchId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\"" + serialNo + "\"," + "signature=\"" + signature + "\"";
        
        Map<String, String> header = new HashMap<String, String>();
        header.put("Authorization", authorization);
        header.put("Content-Type", "application/json");
        
        return header;
    }
    
    private static String sign(byte[] message) {
        try {
            PrivateKey privateKey = WeChatUtils.getPrivateKey();
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 获取私钥。
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    private static PrivateKey getPrivateKey() {
        try {
            String privateKeyPath = JFinal.me().getServletContext().getRealPath(privateKeyFileName);
            String content = new String(Files.readAllBytes(Paths.get(privateKeyPath)), "utf-8");
            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 (Exception e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        }
    }
    
    /**
     * 获取随机字符串
     * @param length
     * @return
     */
    private static String getRandomString(int length){
        //定义一个字符串(A-Z,a-z,0-9)即62位;
        String str="zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        //由Random生成随机数
        Random random=new Random(); 
        StringBuffer sb=new StringBuffer();
        //长度为几就循环几次
        for(int i=0; i<length; ++i){
            //产生0-61的数字
            int number=random.nextInt(62);
            //将产生的数字通过length次承载到sb中
            sb.append(str.charAt(number));
        }
        //将承载的字符转换成字符串
        return sb.toString();
    }
}
package cn.ktc.jkf.api;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.http.util.TextUtils;

import com.jfinal.core.Controller;
import com.jfinal.json.FastJson;
import com.jfinal.kit.Kv;

import cn.ktc.jkf.utils.WeChatUtils;

/**
 * 微信接口
 * @author As40098
 *
 */
public class WeChatApi extends Controller {
    /**
     * 存储权限键名
     */
    private String authKey = "auth";
    
    /**
     * 微信授权,获取openid、session_key
     */
    public void setAuth() {
        String code = get("code");
        String authInfo = WeChatUtils.getAuthInfo(code);
        System.out.println("获取微信授权信息:"+authInfo);
        
        setSessionAttr(this.authKey, authInfo);
        String sessionId = getSession().getId();
        
        Kv data = Kv.by("JSESSIONID", sessionId);
        renderJson(data);
    }
    
    /**
     * 获取手机号码
     */
    public void getPhoneNumber() {
        String encryptedData = get("encryptedData");
        String iv = get("iv");
        String sessionKey = this.getSessionKey();
        
        if(sessionKey == null) {
            renderJson(Kv.by("code", 100).set("message", "登录已过期"));
            return;
        }
        
        String phoneInfo = WeChatUtils.getPhoneNumber(encryptedData, iv, sessionKey);
        System.out.println("获取手机号信息:"+phoneInfo);
        
        renderJson(phoneInfo);
    }
    
    /**
     * 获取支付参数
     */
    public void getPaymentInfo() {
        int price = 1;
        String orderSn = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String openid = this.getOpenid();
        if(openid == null) {
            renderJson(Kv.by("code", 100).set("message", "登录已过期"));
            return;
        }
        // 定义返回结果地址
        String notifyUrl = getRequest().getRequestURL().toString().split(getRequest().getServletPath())[0]+"/wechat/payNotify";
        // 生成预支付订单
        String prepayId = WeChatUtils.unifiedOrder(openid, orderSn, price, notifyUrl);
        System.out.println("预支付获取prepayId="+prepayId);
        if(prepayId == null) {
            renderJson(Kv.by("message", "生成预付单失败"));
            return;
        }
        Map<String, Object> paymentInfo = WeChatUtils.getPaySign(prepayId);
        if(paymentInfo == null) {
            renderJson(Kv.by("message", "支付失败"));
            return;
        }
        paymentInfo.put("orderSn", orderSn);
        
        renderJson(paymentInfo);
    }
    
    /**
     * 支付异步通知
     */
    public void payNotify() {
        String params = getRawData();
        if(!TextUtils.isEmpty(params)) {
            System.out.println("异步通知参数:"+params);
            String result = WeChatUtils.decodeNotify(params);
            if(!TextUtils.isEmpty(result)) {
                Kv data = FastJson.getJson().parse(result, Kv.class);
                if(data != null) {
                    String state = data.getStr("trade_state");
                    if(state.equals("SUCCESS")) {
                        String orderSn = data.getStr("out_trade_no");
                        if(WeChatUtils.checkOrderIsPay(orderSn)) {
                            // 修改订单信息
                            
                            // 返回正确结果为微信
                            renderJson(Kv.by("code", "SUCCESS").set("message","成功"));
                            return;
                        }
                    }
                }
            }
        }
        renderJson(Kv.by("code", "FAIL").set("message","失败"));
    }
    
    /**
     * 检查订单是否已经支付成功
     */
    public void checkOrderPayState() {
        String orderSn = get("orderSn");
        if(!WeChatUtils.checkOrderIsPay(orderSn)) {
            renderJson(Kv.by("code", "FAIL").set("message","订单还未支付成功"));
            return;
        }
        renderJson(Kv.by("code", "SUCCESS"));
    }
    
    /**
     * 获取openid
     * @return
     */
    private String getOpenid() {
        String auth = getSessionAttr(this.authKey);
        Kv data = FastJson.getJson().parse(auth, Kv.class);
        if(data == null) {
            return null;
        }
        return data.getStr("openid");
    }
    
    /**
     * 获取session_key
     * @return
     */
    private String getSessionKey() {
        String auth = getSessionAttr(this.authKey);
        if(auth == null) {
            return null;
        }
        Kv data = FastJson.getJson().parse(auth, Kv.class);
        if(data == null) {
            return null;
        }
        return data.getStr("session_key");
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值