(1)官网注册开发者账号:https://www.showapi.com/
(2)点击验证邮箱
(3)进入控制台,点击 接口使用者 —> 我的应用,获取showapi_appid 和 secret
当前使用的是生成二维码的功能接口,官网中会有很多业务统计或其他功能,大家可以自行了解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.itheima.restkeeper;
import com.alibaba.fastjson.JSONObject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate;
@SpringBootTest @RunWith(SpringRunner.class) public class QrCodeTest {
@Autowired RestTemplate restTemplate;
@Test public void testQRcode(){ String url = "http://route.showapi.com/887-1" + "?showapi_appid=710810" + "&showapi_sign=a35b3ebc52d04d159a9a60fa0335709b"+ "&content=http://www.itcast.cn"+ "&size=8" + "&imgExtName=png"; String showApiSring = restTemplate.getForObject(url, String.class); JSONObject jsonObject = JSONObject.parseObject(showApiSring); String qrcodeUrl = jsonObject.getJSONObject("showapi_res_body").getString("imgUrl"); System.out.println(qrcodeUrl); } }
|
或者是使用 Hutool,Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。
同时提供以下组件:
模块 | 介绍 |
---|
hutool-aop | JDK动态代理封装,提供非IOC下的切面支持 |
hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
hutool-cache | 简单缓存实现 |
hutool-core | 核心,包括Bean操作、日期、各种Util等 |
hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
hutool-db | JDBC封装后的数据操作,基于ActiveRecord思想 |
hutool-dfa | 基于DFA模型的多关键字查找 |
hutool-extra | 扩展模块,对第三方封装 (模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
hutool-http | 基于HttpUrlConnection的Http客户端封装 |
hutool-log | 自动识别日志实现的日志门面 |
hutool-script | 脚本执行封装,例如Javascript |
hutool-setting | 功能更强大的Setting配置文件和Properties封装 |
hutool-system | 系统参数调用封装(JVM信息等) |
hutool-json | JSON实现 |
hutool-captcha | 图片验证码实现 |
hutool-poi | 针对POI中Excel和Word的封装 |
hutool-socket | 基于Java的NIO和AIO的Socket封装 |
hutool-jwt | JSON Web Token (JWT)封装实现 |
可以根据需求对每个模块单独引入,也可以通过引入hutool-all
方式引入所有模块。
1 2 3 4 5
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency>
|
二维码快速生成见文档:https://www.hutool.cn/docs/#/extra/%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%B7%A5%E5%85%B7-QrCodeUtil
依赖:
1 2 3 4 5
| <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.3</version> </dependency>
|
核心代码:
1 2 3 4 5 6 7
| QrCodeUtil.generate("https://hutool.cn/", 300, 300, FileUtil.file("d:/qrcode.jpg"));
String base64 = QrCodeUtil. generateAsBase64("http://www.itheima.com", new QrConfig(300, 300), "png"); System.out.println(base64);
|
很多时候,二维码无法识别,这时就要调整纠错级别。纠错级别使用zxing的ErrorCorrectionLevel
枚举封装,包括:L、M、Q、H几个参数,由低到高。低级别的像素块更大,可以远距离识别,但是遮挡就会造成无法识别。高级别则相反,像素块小,允许遮挡一定范围,但是像素块更密集。
1.2、支付宝支付
1.2.1、准备环境
详细参考:讲义/支付宝支付接入指南.pdf
总结步骤:
注册开放平台账号
注册地址:https://developers.alipay.com/developmentAccess/developmentAccess.htm
支付宝沙箱环境配置
沙箱地址:https://openhome.alipay.com/platform/appDaily.htm
使用支付沙箱需要配置密钥
在线生成秘钥:https://miniu.alipay.com/keytool/create
秘钥相关:https://opendocs.alipay.com/mini/miniu/keytool/create
在沙箱中配置对应的公钥保存即可
目前沙箱钱包仅提供Android版本,可点击下载
注意:使用沙箱账号登录
备注:
- 应用公钥(public key)需提供给支付宝账号管理者上传到支付宝开放平台。
- 应用私钥(private key)由开发者自己保存,需填写到代码中供签名时使用。
- 生成的私钥需妥善保管,避免遗失,不要泄露。
- 密钥和应用(APPID)一一对应,即开发者需要为名下的每个应用分别设置密钥,且不同应用的密钥不能混用。
餐掌柜主要使用 面对面 支付接入方法,详细参见:https://docs.open.alipay.com/203/
1.2.2、公钥私钥概述
大家可以登录支付宝开发者中心进行账号及应用的创建,其入住方式配置这里就不做赘述,大家可以访问网址:https://opendocs.alipay.com/open/0128wr,进行学习:
再接入支付宝中我们需要理解公钥及私钥的使用
对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。
对于对称加密,封装了JDK的,具体介绍见:https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyGenerator:
- AES (默认
AES/ECB/PKCS5Padding
) - ARCFOUR
- Blowfish
- DES (默认
DES/ECB/PKCS5Padding
) - DESede
- RC2
- PBEWithMD5AndDES
- PBEWithSHA1AndDESede
- PBEWithSHA1AndRC2_40
57327523975392075
非对称加密,最常用的就是RSA和DSA,在Hutool中使用AsymmetricCrypto
对象来负责加密解密。
非对称加密有公钥和私钥两个概念,私钥自己拥有,不能给别人,公钥公开。根据应用的不同,我们可以选择使用不同的密钥加密:
- 签名:使用私钥加密,公钥解密。用于让所有公钥所有者验证私钥所有者的身份并且用来防止私钥所有者发布的内容被篡改,但是不用来保证内容不被他人获得。
- 加密:用公钥加密,私钥解密。用于向公钥所有者发布信息,这个信息可能被他人篡改,但是无法被他人获得。
Hutool封装了JDK的,详细见https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator:
- RSA
- RSA_ECB_PKCS1(RSA/ECB/PKCS1Padding)
- RSA_None(RSA/None/NoPadding)
- ECIES(需要Bouncy Castle库)
应用请求三方中私钥公钥的使用:
三方返回信息给应用中私钥公钥的使用:
小结:
公钥加密,私钥解密
应用服务器【餐掌柜】需要保存秘钥:
三方服务器【支付宝/微信】需要保存秘钥:
- 应用服务公钥(上传)
- 支付宝私钥(三方保存,不对外提供)
使用支付宝开发助手创建密钥,选择RSA2的加密方式,如图所示
生成后点击上传公钥,则会跳转对应的支付密钥上传页面
1.2.3、快速入门(沙箱版本)
接口定义:外部商户请求支付宝创建订单并支付
参数参考文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay
官方SDK与DEMO代码:https://opendocs.alipay.com/open/203/105285/
代码实现:
(1)pom依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.9.9</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> </dependencies>
|
(2)必要的参数
如下参数是支付宝接口交互中的必要参数:appId、应用私钥、支付宝公钥,以下参数请学习者自行申请。
AlipayConfig配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.itheima.alipay.config;
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration;
@Configuration @ConfigurationProperties(prefix = "restkeeper.alipay") @Data public class AlipayConfig { public String APPID = ""; public String RSA_PRIVATE_KEY = ""; public String notify_url = "http://商户网关地址/alipay.trade.wap.pay-JAVA-UTF-8/notify_url.jsp"; public String return_url = "http://商户网关地址/alipay.trade.wap.pay-JAVA-UTF-8/return_url.jsp"; public String URL = "https://openapi.alipay.com/gateway.do"; public String CHARSET = "UTF-8"; public String FORMAT = "json"; public String ALIPAY_PUBLIC_KEY = ""; public String log_path = "/log"; public String SIGNTYPE = "RSA2"; }
|
application.yml配置文件:
1 2 3 4 5 6 7 8 9 10 11 12
| restkeeper: alipay: APPID: 2016100200645088 URL: https://openapi.alipaydev.com/gateway.do CHARSET: UTF-8 notify_url: return_url: FORMAT: json ALIPAY_PUBLIC_KEY: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl1NG6w+hLEUwQvXViCSZJFoFY6vdkpK9/PRo7bBSo2JV1TOqHeAGBF0cGlH/GA16sPpXgE+hYIRv0fQLMmQxE54GHGhHCp2qXvqNdcOeUuazGkV70FBIXl0CUdgVkpuVhNCTa4llooCJsFGftD/BtcyW65TcY4GAU5Km+ZoLkgRQIV+cEBWgo4yHdQpJPYDZmMmG5rbY7YsL/gSPsAL1NwjdOH4lBv+IpTWGUJJ5KtLcN7d9F5a2KdEZTuN1JqeROgJp32q2hbpUeat4X/1S1xgpmZ1BJ7LgSALcn9bLmbXJLGc6wZ1sB4kp/TCgrkANJpzOPp7XMGjBXiZ4v6Yl9QIDAQAB RSA_PRIVATE_KEY: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnYRYD+omT9FbR+lfHXVYkGK4MI66NFLaMqm7lexPec0reVbx5bRTgs+l/u7J3Jf5I4d/3v27a51voPqQE2PDM8/r16RRmF946na9l9hYcbRfZHdTFRmoJ+Epd3SahuDRvBm84c3sUdcbQXFFytyYgDdszYukOank8glKHzpwgMVznNEej/q6+aw9Fut2EJONpw6gBtpA5WowsxFcg+cCgLPG1gesruD30/WnOk2zXA4K8OknFh21g7Fd4bU0fNSBWjBD4p4iDlsEQ7krpGrK9/CI/xezREvk5745Y9pDMFRQRYpEAHiFKx+pYgvgfPWWi4me3Na4Y64mt6vbkdWxbAgMBAAECggEAZslyMaNLlXZ5Up2ABkhFPAmD6KSI7s6HhD6tt3MrsnHuyjawdYkNRyh0/iIP6KeGTs+XMJd4xilKAYdmRivLRLGXrigihMeniyuGqQDEd1RvTr/JCBTDzbeSQ64pqSpr2LqE1o/kR55EJ3Rp+B6M5SZdNGNLZ7TvGr+VWx0AN6vXrQcX+W0Fmju6725RiSZqihAVNTpHbWNUZbqZIV/W/8pmwNW/2or59PCIyWeaBK8+4hIH1Hx5viAXYLSK4brOlP7w6uzzy+d49hNVrII8guPQ+9WF/CNg4og7loifVH5Dyd665uyny/0dBZf5IPVl0UF8OlY/wAJ6Y/10ue/yEQKBgQDwGBN260uqKOs5nIIPiILiIbKmF79iSstJwh3LJJGtA62NDCyA95LRddIv+FTkZ6F7kagSuOs4TSFtAIWm8tj0MufdGzsSmYabXE1DDNsUuNbXDyd1Ii6KtYN7oMHxzm0PPi6jGefgqS5yq8bG+28APVHQNYnwyuvS0w27J/azrwKBgQCyd8nUjCQvJJxWLnyxy352Xpr+CcXxKYHnKBwDm94elG+TnA/IxoE4iqdKT9yhRCI0pqQDbwyaRm0iXNviVS+oMLwyRotesQ+/ViFBQw26j4Fmaq2LQFti6mSHQjw+BLtxdIy1pVVxDxPBXg6zYRvq5Q3T/+fmbrfB6DxZcgsBFQKBgQC0N848gEfudQKD3xe9YyGjbdn0VHUC6dOIDN5iQpPag50893tcXvlkooTgHw5R1/vdjirTytw9CaBienbYJwd03dUvIaaIwpbIfVM9ViQIfOo+yZA7mynGUpNcNAIAaItyWqGVKffkqflEd+4gJFFgo6aKm/VrulWjjWqMJmZG3wKBgGaVko85SudKTQ8Aw65TQUr7EG5r4brA2CmuFYRBiQjc29HmR/BpogeFM6n0g+ayylKnYumSYJUhXEP/Smkr/Cvab6Mah6wTbPDXql/gEjklmgTr1vuPL7iI8OYKvaQMhk4t51/WPGmzd/CThzG25Rw9M5ijpYIALGIqgt4LPqYNAoGAFk8hYTT5PU1tNmCi/VVcsNNc4GlDytpgitP0nbBrp/XhjIBEjF1kxW/BrshIuQLzZ74jnM3P0Q6i9vtuqMHv5/Lcx9PSprCxCNSAn7W1VNmey2reqBtTFRo485AtsSntjGiaoORzpBKRZHBIMLSS8zEY+BSBwecaktF6IkmVuuI= log_path: SIGNTYPE: RSA2
|
(3)编写Controller实现
注意:此处使用的是通用版,开发时使用简易版本,具体看官方文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package com.itheima.alipay.controller;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.itheima.alipay.config.AlipayConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@RestController public class PayController {
@Autowired AlipayConfig alipayConfig;
@GetMapping("/alipaytest") public void alipaytest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException { AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getURL(), alipayConfig.getAPPID(), alipayConfig.getRSA_PRIVATE_KEY(), alipayConfig.getFORMAT(), alipayConfig.getCHARSET(), alipayConfig.getALIPAY_PUBLIC_KEY(), alipayConfig.getSIGNTYPE()); AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); alipayRequest.setBizContent("{" + "\"out_trade_no\":\"20150321210101123\"," + " \"total_amount\":\"0.01\"," + " \"subject\":\"Iphone6 16G\"," + " \"product_code\":\"QUICK_WAP_PAY\"" + " }"); String form = ""; try { form = alipayClient.pageExecute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } httpResponse.setContentType("text/html;charset=" + alipayConfig.getCHARSET()); httpResponse.getWriter().write(form); httpResponse.getWriter().flush(); httpResponse.getWriter().close(); }
}
|
(4)启动项目测试,使用 ==支付宝沙箱版== 付款
需要和支付宝建立连接,所以我们需要内网穿透工具 将当前请求的地址变成公网可以访问的地址测试。
准备内网穿透工具 小米球或 Natapp,注册配置
启动内网穿透服务
将 请求地址修改为: http://itheima.ngrok2.xiaomiqiu.cn/alipaytest
将上述地址 生成对应的二维码
支付宝扫描二维码完成支付
在沙箱环境完成功能调试后,必须将支付宝网关、APPID、应用私钥、支付宝公钥修改成正式环境的配置,并在蚂蚁正式环境进行完整的功能验收测试。
1.2.4、快速入门(上线版本)
当前使用SDK(easy-sdk)
(1)引入依赖
1 2 3 4 5 6
| <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-easysdk</artifactId> <version>2.2.0</version> </dependency>
|
(2)配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| restkeeper: easyalipay: protocol: https gatewayHost: openapi.alipay.com signType: RSA2 appId: 2021002173680104 merchantPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCYTgKj2xEIJi7lqjGRuQ/XWNDRuhoMtWvQg1DzF3fDWodh6WMed5V7mi3s4zXLnm4TvHVazgLnVXJlONxG4EsVJRGp3hY1fSGAAcByRC2E8E+NdhIvaOAWUuEid6AeshfNqmePnJyHxpxf9ZYn0Bij0kM8yMwv1izkBmsuydXroQegYfias6HT+CQ9mgU0awb6ZPeGqC6sw9Tv991jOohGv4xgEVjRh69pvB8Eo1vubYJ8mhOEf5xwWkx8/n7tDa4uA7ioUlgLRxhtOkCkpTQXzLAzm8gxoJlEYL/sFR3plaPBRAafWoKdfF55W0SBXDhhNqqce+r1H4pw4IyZxqyrAgMBAAECggEAYAcnoPpZlcLFZObXFCMTutpz5xgonoSwsqppGqxcRZ7Jp1FIvof1hxYiCK8FVxnQG7+CWrtzlzoHw4yDTmjSzkUuCuVNKXJ48cWo+iLEdII0FmQweRXt3AVrj5jPKyts2K6tVx4Oj4kJRXOJthZ9wqSq4iNUooCukyL852Y467P/Me+9Vr2vQb117qLaNTwvR2GV6OZUNQPl7qKNsp86e5lREIBHPk8uhQO3KS+QPPvTDkKBH+bNo7Bu4L4J0ITdvjU9FupaQ6xfFCOrgu7P2bw9Mk2JM6jHp1hEjuoLVGA97sL6CSPhQu9s9KJbup2DGOI1qQoEATBypHFueYv4yQKBgQD57xI1iPUl3tVNH5e3yYa08NLS+mV9N3tRTjIWHbMwQKMbraaO+dGQRGiytjejao3y0EVFuqOuhbXAxtvb1pUukASqY3zW01zzVNNw8nO3zeVPo+xWLAvlnyX3MekKMYjo1dAyWBzXuPgo3D413nMiPZ8oOgtbpTyu/dTeN3NJTQKBgQCcAFWSUinmZ3x9Xr779CeX1MsKG+++C4iLP7vNP8Lf8IcPE8NnHYZTQqDuvq1Itai7UbhZX95itYqjp9SlXT4hSrMGI7qwobJ0vXxXrNN6VwZtz1N75vnIrZNHnTFMWTUBKCySLcpsqs7qCSQEv6luOQUSUH/0gaN6txOq5W4R1wKBgH4GdLIV6zc7U2beJUyBC7G1NTk5FW+8SCxJN6w7MZ2FGjncp/20Ll2GgRyMESYPlp/3MNbmM57OwUUBgN8rJnIiIJgiLlLMpTP1c+CiAIOQCK7Nw1/4Oc+BHk21FwMS0yxElASutWx5UniYBa54CqobVGOeURfXC/BZAbtDTpiJAoGAMR3B03HfE1Xd0jM0emti0+EBlEs7bmB/Oyhz3qmGl69JNqwIR7z5/9johoKuWEgpueB+5FTU1ctGvUQoJXB4EU9Nkk9JhjdC0pKeRZR6ePhRY9108XvFhTNxPYj2bo1frN+TOOsF4rTctL7wAja+B6AYQq3pu3fdmtNtc88Mmr0CgYEAuDD/PzVTLMYgEqq0ruZcCyFd0HwMV8KoC07aXXeLZT/IGtoJASFkWIxcoyK8NVZvDqoGkpaLAw3G7CmzoxpqSvyMWwHGdZy7KDV55g5O9r4Lt/dti7xt6gBYm8XB1X7cLcu0x9lYs6KL01To+Ep8DxSO7LUODQ0Utu3Pkf9W1qc= alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhUnjdAKwZApwZEcfq+5L0pa77Vg3mqcoXv+th8RR0SYotkPsH1f2JkbS48ySaSCM6YNWSMNfqp5qdOla2zUJOBnJ/yaBg7s7fVD6V3M2mEog8kCDYGKt/3P4VII3xYl8lFYMQ3IcFRELkxCBBCA8JDKmf5z2R4F/Z/jFFEuOwxaJvp+7Ke9OzZHYdWGNnU6QP8YYLYUeX7VNZLHEuly34ExAw6A+yJkNDsYEho2Lu31QjT2pLh9g+88MlRfiI92iN25O9NVdeM4f5RcpvBPrBQZQs9tlFmALYSFS3prIf3FAobWM+W7iwxT6J25nFIhst1DdJQfIBpaeRUJVTkn99QIDAQAB encryptKey: 3GHtw6uVpqjtQGeJc+eEqQ== notifyUrl:
|
(3)属性配置类AliPayProperties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.itheima.alipay.prop;
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration;
@Configuration @Data @ConfigurationProperties(prefix = "restkeeper.easyalipay") public class AliPayProperties {
private String protocol; private String gatewayHost; private String signType; private String appId; private String merchantPrivateKey; private String alipayPublicKey; private String notifyUrl; private String encryptKey; }
|
(4)配置类MyAlipayConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package com.itheima.alipay.config;
import com.alipay.easysdk.kernel.Config; import com.itheima.alipay.prop.AliPayProperties; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @Data public class MyAlipayConfig {
@Bean public Config config(AliPayProperties payProperties) { Config config = new Config(); config.protocol = payProperties.getProtocol(); config.gatewayHost = payProperties.getGatewayHost(); config.signType = payProperties.getSignType();
config.appId = payProperties.getAppId();
config.merchantPrivateKey = payProperties.getMerchantPrivateKey();
config.alipayPublicKey = payProperties.getAlipayPublicKey();
config.notifyUrl = "";
config.encryptKey = "";
return config; } }
|
(5)编写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| package com.itheima.alipay.controller;
import com.alibaba.fastjson.JSON; import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.kernel.Config; import com.alipay.easysdk.kernel.util.ResponseChecker; import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse; import com.alipay.easysdk.payment.common.models.AlipayTradeCreateResponse; import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse; import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse; import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map;
@RestController @RequestMapping("easy") public class EasyPayController {
@Autowired Config config;
@GetMapping("pay/{code}") public String pay(@PathVariable String code) { Factory.setOptions(config); try { AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace() .preCreate("Apple iPhone11 128G", code, "0.01");
if (ResponseChecker.success(response)) {
System.out.println("调用成功"); String body = response.getHttpBody(); System.out.println(body);
return response.getHttpBody(); } else { System.err.println("调用失败,原因:" + response.toString()); } } catch (Exception e) { System.err.println("调用遭遇异常,原因:" + e.getMessage()); throw new RuntimeException(e.getMessage(), e); }
return "ERROR"; }
@GetMapping("querypay/{code}") public String querypay(@PathVariable String code) { Factory.setOptions(config);
try { AlipayTradeQueryResponse response = Factory.Payment.Common().query(code);
Map<String, Map<String, String>> map = JSON.parseObject(response.getHttpBody(), Map.class); if (map.get("alipay_trade_query_response").get("trade_status").equals("TRADE_SUCCESS")) { System.out.println("支付成功......"); System.out.println("处理订单后续操作....."); }
return response.getHttpBody();
} catch (Exception e) { System.err.println("调用遭遇异常,原因:" + e.getMessage()); throw new RuntimeException(e.getMessage(), e); }
}
}
|
1.3、微信支付
1.3.1、准备环境
详细参考:讲义/微信支付接入指南.pdf
餐掌柜C扫B需求要求可以手机网页交互,微信JSAPI/NATIVE支付符合需求。
总结步骤:
- 以企业身份注册微信公众号 https://mp.weixin.qq.com/
- 登录公众号,点击左侧菜单 “微信支付” 开通微信支付,需要提供营业执照、身份证等信息。
- 开通微信支付后即可在微信商户平台(pay.weixin.qq.com)开通JSAPI/NATIVE支付。
- 获取openid,openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户,同时也是微信JSAPI支付的必传参数。
- 调用统一下单API,调取支付接口即可
1.3.2、快速入门
参考文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
V3新版本:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
JSAPI接口定义:
- 用户同意授权,获取code授权码
- 通过code换取网页授权access_token和openid
- 调用JSAPI/NATIVE 统一下单 接口
官方SDK与DEMO代码:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
NATIVE代码实现:
(1)添加依赖
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.github.tedzhdz</groupId> <artifactId>wxpay-sdk</artifactId> <version>3.0.10</version> </dependency>
|
(2)编写配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.itheima.wxpay.config;
import com.github.wxpay.sdk.IWXPayDomain; import com.github.wxpay.sdk.WXPayConfig; import com.github.wxpay.sdk.WXPayConstants;
import java.io.InputStream;
public class WXPayConfigCustom extends WXPayConfig {
@Override protected String getAppID() { return "wx8397f8696b538317"; }
@Override protected String getMchID() { return "1473426802"; }
@Override protected String getKey() { return "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"; }
@Override protected InputStream getCertStream() { return null; }
@Override protected IWXPayDomain getWXPayDomain() { return new IWXPayDomain() { @Override public void report(String s, long l, Exception e) { }
@Override public DomainInfo getDomain(WXPayConfig wxPayConfig) { return new DomainInfo(WXPayConstants.DOMAIN_API, true); } }; } }
|
(3)编写下单测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| package com.itheima.wxpay.controller.v3;
import cn.hutool.http.HttpUtil; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayUtil; import com.itheima.wxpay.config.WXPayConfigCustom; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map;
@Slf4j @RestController @RequestMapping("wxpay") public class WxpayController {
@RequestMapping("notify") public String payNotify(HttpServletRequest request, HttpServletResponse response) { try { String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding()); Map<String, String> map = WXPayUtil.xmlToMap(xmlResult);
String orderId = map.get("out_trade_no"); String tradeNo = map.get("transaction_id"); String totalFee = map.get("total_fee"); String returnCode = map.get("return_code"); String resultCode = map.get("result_code");
return WxPayNotifyResponse.success("处理成功!"); } catch (Exception e) { log.error("微信回调结果异常,异常原因{}", e.getMessage()); return WxPayNotifyResponse.fail(e.getMessage()); } }
@GetMapping("unifiedOrder/{code}") public String unifiedOrder(@PathVariable String code) throws Exception { WXPayConfigCustom config = new WXPayConfigCustom(); WXPay wxpay = new WXPay(config);
Map<String, String> data = new HashMap<String, String>(); data.put("body", "餐掌柜-餐饮消费");
data.put("out_trade_no", code); data.put("device_info", ""); data.put("fee_type", "CNY"); data.put("total_fee", "1"); data.put("spbill_create_ip", "123.12.12.123"); data.put("notify_url", "http://itheima.ngrok2.xiaomiqiu.cn/wxpay/notify"); data.put("trade_type", "NATIVE");
try { Map<String, String> resp = wxpay.unifiedOrder(data); System.out.println(resp); return resp.get("code_url"); } catch (Exception e) { e.printStackTrace(); }
return "OK"; }
}
|