diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 335fe3a3..759ee7ff 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -59,7 +59,11 @@ xml-path 3.0.1 - + + com.github.binarywang + qrcode-utils + 1.0 + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java index 79787744..d0b21109 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java @@ -7,6 +7,7 @@ import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; import me.chanjar.weixin.mp.bean.pay.result.*; +import java.io.File; import java.util.Map; /** @@ -235,4 +236,32 @@ public interface WxMpPayService { */ WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException; + /** + *
+   * 扫码支付模式一生成二维码的方法
+   * 二维码中的内容为链接,形式为:
+   * weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
+   * 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
+   * 
+ * @param productId 产品Id + * @param sideLength 要生成的二维码的边长,如果为空,则取默认值400 + * @param logoFile 商户logo图片的文件对象,可以为空 + * @return 生成的二维码的字节数组 + */ + byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength); + + /** + *
+   * 扫码支付模式二生成二维码的方法
+   * 对应链接格式:weixin://wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。
+   * 该模式链接较短,生成的二维码打印到结账小票上的识别率较高。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
+   * 
+ * @param codeUrl 微信返回的交易会话的二维码链接 + * @param logoFile 商户logo图片的文件对象,可以为空 + * @param sideLength 要生成的二维码的边长,如果为空,则取默认值400 + * @return 生成的二维码的字节数组 + */ + byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java index 8103993c..33bf6cdf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java @@ -1,5 +1,7 @@ package me.chanjar.weixin.mp.api.impl; +import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.google.common.collect.Maps; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.BeanUtils; @@ -25,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; +import java.io.File; import java.io.IOException; import java.util.*; @@ -316,6 +319,41 @@ public class WxMpPayServiceImpl implements WxMpPayService { return result; } + @Override + public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { + //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX + StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); + Map params = Maps.newHashMap(); + params.put("appid", this.getConfig().getAppId()); + params.put("mch_id", this.getConfig().getPartnerId()); + params.put("product_id", productId); + params.put("time_stamp", String.valueOf(System.currentTimeMillis())); + params.put("nonce_str", String.valueOf(System.currentTimeMillis())); + + String sign = this.createSign(params); + params.put("sign", sign); + + for (String key : params.keySet()) { + codeUrl.append(key + "=" + params.get(key) + "&"); + } + + String content = codeUrl.toString().substring(0, codeUrl.length() - 1); + if (sideLength == null || sideLength < 1) { + return QrcodeUtils.createQrcode(content, logoFile); + } + + return QrcodeUtils.createQrcode(content, sideLength, logoFile); + } + + @Override + public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { + if (sideLength == null || sideLength < 1) { + return QrcodeUtils.createQrcode(codeUrl, logoFile); + } + + return QrcodeUtils.createQrcode(codeUrl, sideLength, logoFile); + } + private String executeRequest(String url, String requestStr) throws WxErrorException { HttpPost httpPost = new HttpPost(url); if (this.wxMpService.getHttpProxy() != null) { diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java index 13491d20..c506df0f 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java @@ -19,7 +19,6 @@ public class ApiTestModule implements Module { WxXmlMpInMemoryConfigStorage config = this .fromXml(WxXmlMpInMemoryConfigStorage.class, is1); config.setAccessTokenLock(new ReentrantLock()); - config.setSslContextFilePath(config.getKeyPath()); WxMpService wxService = new WxMpServiceImpl(); wxService.setWxMpConfigStorage(config); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java index 418b710b..248fc7dd 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.mp.api.impl; +import com.github.binarywang.utils.qrcode.QrcodeUtils; import com.google.inject.Inject; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.ApiTestModule; @@ -10,9 +11,13 @@ import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; import me.chanjar.weixin.mp.bean.pay.result.*; +import org.testng.Assert; import org.testng.annotations.Guice; import org.testng.annotations.Test; +import java.nio.file.Files; +import java.nio.file.Path; + /** * 测试支付相关接口 * Created by Binary Wang on 2016/7/28. @@ -22,7 +27,6 @@ import org.testng.annotations.Test; @Test @Guice(modules = ApiTestModule.class) public class WxMpPayServiceImplTest { - @Inject protected WxMpService wxService; @@ -32,9 +36,17 @@ public class WxMpPayServiceImplTest { } /** - * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest)} . + * 需要证书的接口需要先执行该方法 */ @Test + public void setSSLKey(){ + WxXmlMpInMemoryConfigStorage config = (WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage(); + config.setSslContextFilePath(config.getKeyPath()); + } + /** + * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest)} . + */ + @Test(dependsOnMethods = {"setSSLKey"}) public void testRefund() throws Exception { WxPayRefundRequest request = new WxPayRefundRequest(); request.setOutRefundNo("aaa"); @@ -45,7 +57,6 @@ public class WxMpPayServiceImplTest { System.err.println(result); } - /** * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refundQuery(String, String, String, String)} . */ @@ -63,15 +74,10 @@ public class WxMpPayServiceImplTest { System.err.println(result); } - @Test - public void testCheckJSSDKCallbackDataSignature() throws Exception { - - } - /** * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest)} . */ - @Test + @Test(dependsOnMethods = {"setSSLKey"}) public void testSendRedpack() throws Exception { WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); request.setActName("abc"); @@ -86,7 +92,7 @@ public class WxMpPayServiceImplTest { /** * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String)}. */ - @Test + @Test(dependsOnMethods = {"setSSLKey"}) public void testQueryRedpack() throws Exception { WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa"); System.err.println(redpackResult); @@ -125,7 +131,7 @@ public class WxMpPayServiceImplTest { /** * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest)}. */ - @Test + @Test(dependsOnMethods = {"setSSLKey"}) public final void testEntPay() throws WxErrorException { WxEntPayRequest request = new WxEntPayRequest(); System.err.println(this.wxService.getPayService().entPay(request)); @@ -134,8 +140,33 @@ public class WxMpPayServiceImplTest { /** * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryEntPay(String)}. */ - @Test + @Test(dependsOnMethods = {"setSSLKey"}) public final void testQueryEntPay() throws WxErrorException { System.err.println(this.wxService.getPayService().queryEntPay("11212121")); } + + @Test + public void testCreateScanPayQrcodeMode1() throws Exception { + String productId = "abc"; + byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode1(productId, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); + System.out.println(qrcodeContent); + + Assert.assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); + Assert.assertTrue(qrcodeContent.contains("product_id=" + productId)); + } + + @Test + public void testCreateScanPayQrcodeMode2() throws Exception { + String qrcodeContent = "abc"; + byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode2(qrcodeContent, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + + Assert.assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); + } + + }