package com.example.pay.controller; import com.example.pay.configuration.WXPayClient; import com.example.pay.util.PayUtil; import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayConstants; import com.github.wxpay.sdk.WXPayUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; /** * 微信支付-扫码支付. * <p> * detailed description * * @author Mengday Zhang * @version 1.0 * @since 2018/6/18 */ @Slf4j @RestController @RequestMapping("/wxpay/precreate") public class WXPayPrecreateController { @Autowired private WXPay wxPay; @Autowired private WXPayClient wxPayClient; /** * 扫码支付 - 统一下单 * 相当于支付不的电脑网站支付 * * <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1">扫码支付API</a> */ @PostMapping("/order") public void precreate(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, String> reqData = new HashMap<>(); reqData.put("out_trade_no", String.valueOf(System.nanoTime())); reqData.put("trade_type", "NATIVE"); reqData.put("product_id", "1"); reqData.put("body", "商户下单"); // 订单总金额,单位为分 reqData.put("total_fee", "2"); // APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 reqData.put("spbill_create_ip", "14.23.150.211"); // 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 reqData.put("notify_url", "http://3sbqi7.natappfree.cc/wxpay/precreate/notify"); // 自定义参数, 可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB" reqData.put("device_info", ""); // 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 reqData.put("attach", ""); /** * { * code_url=weixin://wxpay/bizpayurl?pr=vvz4xwC, * trade_type=NATIVE, * return_msg=OK, * result_code=SUCCESS, * return_code=SUCCESS, * prepay_id=wx18111952823301d9fa53ab8e1414642725 * } */ Map<String, String> responseMap = wxPay.unifiedOrder(reqData); log.info(responseMap.toString()); String returnCode = responseMap.get("return_code"); String resultCode = responseMap.get("result_code"); if (WXPayConstants.SUCCESS.equals(returnCode) && WXPayConstants.SUCCESS.equals(resultCode)) { String prepayId = responseMap.get("prepay_id"); String codeUrl = responseMap.get("code_url"); BufferedImage image = PayUtil.getQRCodeImge(codeUrl); response.setContentType("image/jpeg"); response.setHeader("Pragma","no-cache"); response.setHeader("Cache-Control","no-cache"); response.setIntHeader("Expires",-1); ImageIO.write(image, "JPEG", response.getOutputStream()); } } /** * * @param request * @return * @throws Exception */ @RequestMapping("/notify") public void precreateNotify(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, String> reqData = wxPayClient.getNotifyParameter(request); /** * { * transaction_id=4200000138201806180751222945, * nonce_str=aaaf3fe4d3aa44d8b245bc6c97bda7a8, * bank_type=CFT, * openid=xxx, * sign=821A5F42F5E180ED9EF3743499FBCF13, * fee_type=CNY, * mch_id=xxx, * cash_fee=1, * out_trade_no=186873223426017, * appid=xxx, * total_fee=1, * trade_type=NATIVE, * result_code=SUCCESS, * time_end=20180618131247, * is_subscribe=N, * return_code=SUCCESS * } */ log.info(reqData.toString()); // 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。 boolean signatureValid = wxPay.isPayResultNotifySignatureValid(reqData); if (signatureValid) { /** * 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 * 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态, * 判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。 * 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 */ Map<String, String> responseMap = new HashMap<>(2); responseMap.put("return_code", "SUCCESS"); responseMap.put("return_msg", "OK"); String responseXml = WXPayUtil.mapToXml(responseMap); response.setContentType("text/xml"); response.getWriter().write(responseXml); response.flushBuffer(); } } }