- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章JAVA微信扫码支付模式二线上支付功能实现以及回调由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1、准备工作 。
首先吐槽一下微信关于支付这块,本身支持的支付模式就好几种,但是官方文档特别零散,连像样的Java相关的demo也没几个。本人之前没有搞过微信支付,一开始真是被它搞晕,折腾两天终于调通了,特此写下来,以享后人吧! 。
关于准备工作,就“微信扫码支付模式二”官方文档地址在这 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,实际上需要准备的东西有以下几个:
其中APP_ID和APP_SECRET可以在公众平台找着,MCH_ID和API_KEY则在商户平台找到,特别是API_KEY要在商户平台设置好,对于“微信扫码支付模式二”(支付与回调)实际只会用到APP_ID、MCH_ID和API_KEY,其他的都不用.
关于开发环境,我就不罗嗦了,不管你是springMVC、struts2又或者直接serverlet,都差不多,只要你能保证对应的方法能调用起来就行。关于引用第三方的jar包,我这里只用到了一个xml操作的jdom,记住是1.*的版本,不是官网上最新的2.*,两者不兼容。具体是jdom-1.1.3.jar,依赖包jaxen-1.1.6.jar,就这两个包,我没用到有些例子中使用的httpclient,感觉没必要,而且依赖包特别繁杂,当然你是maven当我没说.
2、开发实战 。
1、首先是接入微信接口,获取微信支付二维码.
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
|
public
String weixin_pay()
throws
Exception {
// 账号信息
String appid = PayConfigUtil.APP_ID;
// appid
//String appsecret = PayConfigUtil.APP_SECRET; // appsecret
String mch_id = PayConfigUtil.MCH_ID;
// 商业号
String key = PayConfigUtil.API_KEY;
// key
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(
8
, currTime.length());
String strRandom = PayCommonUtil.buildRandom(
4
) +
""
;
String nonce_str = strTime + strRandom;
String order_price =
1
;
// 价格 注意:价格的单位是分
String body =
"goodssssss"
;
// 商品名称
String out_trade_no =
"11338"
;
// 订单号
// 获取发起电脑 ip
String spbill_create_ip = PayConfigUtil.CREATE_IP;
// 回调接口
String notify_url = PayConfigUtil.NOTIFY_URL;
String trade_type =
"NATIVE"
;
SortedMap<Object,Object> packageParams =
new
TreeMap<Object,Object>();
packageParams.put(
"appid"
, appid);
packageParams.put(
"mch_id"
, mch_id);
packageParams.put(
"nonce_str"
, nonce_str);
packageParams.put(
"body"
, body);
packageParams.put(
"out_trade_no"
, out_trade_no);
packageParams.put(
"total_fee"
, order_price);
packageParams.put(
"spbill_create_ip"
, spbill_create_ip);
packageParams.put(
"notify_url"
, notify_url);
packageParams.put(
"trade_type"
, trade_type);
String sign = PayCommonUtil.createSign(
"UTF-8"
, packageParams,key);
packageParams.put(
"sign"
, sign);
String requestXML = PayCommonUtil.getRequestXml(packageParams);
System.out.println(requestXML);
String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
Map map = XMLUtil.doXMLParse(resXml);
//String return_code = (String) map.get("return_code");
//String prepay_id = (String) map.get("prepay_id");
String urlCode = (String) map.get(
"code_url"
);
return
urlCode;
}
|
如果不出意外的话,这里就从微信服务器获取了一个支付url,形如weixin://wxpay/bizpayurl?pr=pIxXXXX,之后我们就需要把这个url生成一个二维码,然后就可以使用自己手机微信端扫码支付了。关于二维码生成有很多种方法,各位各取所需吧,我这里提供一个google的二维码生成接口:
1
2
3
4
5
6
7
8
9
10
|
public
static
String QRfromGoogle(String chl)
throws
Exception {
int
widhtHeight =
300
;
String EC_level =
"L"
;
int
margin =
0
;
chl = UrlEncode(chl);
String QRfromGoogle =
"http://chart.apis.google.com/chart?chs="
+ widhtHeight +
"x"
+ widhtHeight
+
"&cht=qr&chld="
+ EC_level +
"|"
+ margin +
"&chl="
+ chl;
return
QRfromGoogle;
}
|
1
2
3
4
|
// 特殊字符处理
public
static
String UrlEncode(String src)
throws
UnsupportedEncodingException {
return
URLEncoder.encode(src,
"UTF-8"
).replace(
"+"
,
"%20"
);
}
|
上面代码中涉及到几个工具类:PayConfigUtil、PayCommonUtil、HttpUtil和XMLUtil,其中PayConfigUtil放的就是上面提到一些配置及路径,PayCommonUtil涉及到了获取当前事件、产生随机字符串、获取参数签名和拼接xml几个方法,代码如下:
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
public
class
PayCommonUtil {
/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
* @return boolean
*/
public
static
boolean
isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb =
new
StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while
(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if
(!
"sign"
.equals(k) &&
null
!= v && !
""
.equals(v)) {
sb.append(k +
"="
+ v +
"&"
);
}
}
sb.append(
"key="
+ API_KEY);
//算出摘要
String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String)packageParams.get(
"sign"
)).toLowerCase();
//System.out.println(tenpaySign + " " + mysign);
return
tenpaySign.equals(mysign);
}
/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param parameters
* 请求参数
* @return
*/
public
static
String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
StringBuffer sb =
new
StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while
(it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if
(
null
!= v && !
""
.equals(v) && !
"sign"
.equals(k) && !
"key"
.equals(k)) {
sb.append(k +
"="
+ v +
"&"
);
}
}
sb.append(
"key="
+ API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return
sign;
}
/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
*/
public
static
String getRequestXml(SortedMap<Object, Object> parameters) {
StringBuffer sb =
new
StringBuffer();
sb.append(
"<xml>"
);
Set es = parameters.entrySet();
Iterator it = es.iterator();
while
(it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if
(
"attach"
.equalsIgnoreCase(k) ||
"body"
.equalsIgnoreCase(k) ||
"sign"
.equalsIgnoreCase(k)) {
sb.append(
"<"
+ k +
">"
+
"<![CDATA["
+ v +
"]]></"
+ k +
">"
);
}
else
{
sb.append(
"<"
+ k +
">"
+ v +
"</"
+ k +
">"
);
}
}
sb.append(
"</xml>"
);
return
sb.toString();
}
/**
* 取出一个指定长度大小的随机正整数.
*
* @param length
* int 设定所取出随机数的长度。length小于11
* @return int 返回生成的随机数。
*/
public
static
int
buildRandom(
int
length) {
int
num =
1
;
double
random = Math.random();
if
(random <
0.1
) {
random = random +
0.1
;
}
for
(
int
i =
0
; i < length; i++) {
num = num *
10
;
}
return
(
int
) ((random * num));
}
/**
* 获取当前时间 yyyyMMddHHmmss
*
* @return String
*/
public
static
String getCurrTime() {
Date now =
new
Date();
SimpleDateFormat outFormat =
new
SimpleDateFormat(
"yyyyMMddHHmmss"
);
String s = outFormat.format(now);
return
s;
}
}
|
HttpUtil和XMLUtil如下:
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
|
public
class
HttpUtil {
private
static
final
Log logger = Logs.get();
private
final
static
int
CONNECT_TIMEOUT =
5000
;
// in milliseconds
private
final
static
String DEFAULT_ENCODING =
"UTF-8"
;
public
static
String postData(String urlStr, String data){
return
postData(urlStr, data,
null
);
}
public
static
String postData(String urlStr, String data, String contentType){
BufferedReader reader =
null
;
try
{
URL url =
new
URL(urlStr);
URLConnection conn = url.openConnection();
conn.setDoOutput(
true
);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if
(contentType !=
null
)
conn.setRequestProperty(
"content-type"
, contentType);
OutputStreamWriter writer =
new
OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if
(data ==
null
)
data =
""
;
writer.write(data);
writer.flush();
writer.close();
reader =
new
BufferedReader(
new
InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb =
new
StringBuilder();
String line =
null
;
while
((line = reader.readLine()) !=
null
) {
sb.append(line);
sb.append(
"\r\n"
);
}
return
sb.toString();
}
catch
(IOException e) {
logger.error(
"Error connecting to "
+ urlStr +
": "
+ e.getMessage());
}
finally
{
try
{
if
(reader !=
null
)
reader.close();
}
catch
(IOException e) {
}
}
return
null
;
}
}
|
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
|
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
|
当然还有一个MD5计算工具类 。
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
|
public
class
MD5Util {
private
static
String byteArrayToHexString(
byte
b[]) {
StringBuffer resultSb =
new
StringBuffer();
for
(
int
i =
0
; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return
resultSb.toString();
}
private
static
String byteToHexString(
byte
b) {
int
n = b;
if
(n <
0
)
n +=
256
;
int
d1 = n /
16
;
int
d2 = n %
16
;
return
hexDigits[d1] + hexDigits[d2];
}
public
static
String MD5Encode(String origin, String charsetname) {
String resultString =
null
;
try
{
resultString =
new
String(origin);
MessageDigest md = MessageDigest.getInstance(
"MD5"
);
if
(charsetname ==
null
||
""
.equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
}
catch
(Exception exception) {
}
return
resultString;
}
private
static
final
String hexDigits[] = {
"0"
,
"1"
,
"2"
,
"3"
,
"4"
,
"5"
,
"6"
,
"7"
,
"8"
,
"9"
,
"a"
,
"b"
,
"c"
,
"d"
,
"e"
,
"f"
};
}
|
2、支付回调 。
支付完成后,微信会把相关支付结果和用户信息发送到我们上面指定的那个回调地址,我们需要接收处理,并返回应答。对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒) 。
关于支付回调接口,我们首先要对于支付结果通知的内容进行签名验证,然后根据支付结果进行相应的处理流程即可.
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
|
public
void
weixin_notify(HttpServletRequest request,HttpServletResponse response)
throws
Exception{
//读取参数
InputStream inputStream ;
StringBuffer sb =
new
StringBuffer();
inputStream = request.getInputStream();
String s ;
BufferedReader in =
new
BufferedReader(
new
InputStreamReader(inputStream,
"UTF-8"
));
while
((s = in.readLine()) !=
null
){
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> m =
new
HashMap<String, String>();
m = XMLUtil.doXMLParse(sb.toString());
//过滤空 设置 TreeMap
SortedMap<Object,Object> packageParams =
new
TreeMap<Object,Object>();
Iterator it = m.keySet().iterator();
while
(it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = m.get(parameter);
String v =
""
;
if
(
null
!= parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
// 账号信息
String key = PayConfigUtil.API_KEY;
// key
logger.info(packageParams);
//判断签名是否正确
if
(PayCommonUtil.isTenpaySign(
"UTF-8"
, packageParams,key)) {
//------------------------------
//处理业务开始
//------------------------------
String resXml =
""
;
if
(
"SUCCESS"
.equals((String)packageParams.get(
"result_code"
))){
// 这里是支付成功
//////////执行自己的业务逻辑////////////////
String mch_id = (String)packageParams.get(
"mch_id"
);
String openid = (String)packageParams.get(
"openid"
);
String is_subscribe = (String)packageParams.get(
"is_subscribe"
);
String out_trade_no = (String)packageParams.get(
"out_trade_no"
);
String total_fee = (String)packageParams.get(
"total_fee"
);
logger.info(
"mch_id:"
+mch_id);
logger.info(
"openid:"
+openid);
logger.info(
"is_subscribe:"
+is_subscribe);
logger.info(
"out_trade_no:"
+out_trade_no);
logger.info(
"total_fee:"
+total_fee);
//////////执行自己的业务逻辑////////////////
logger.info(
"支付成功"
);
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml =
"<xml>"
+
"<return_code><![CDATA[SUCCESS]]></return_code>"
+
"<return_msg><![CDATA[OK]]></return_msg>"
+
"</xml> "
;
}
else
{
logger.info(
"支付失败,错误信息:"
+ packageParams.get(
"err_code"
));
resXml =
"<xml>"
+
"<return_code><![CDATA[FAIL]]></return_code>"
+
"<return_msg><![CDATA[报文为空]]></return_msg>"
+
"</xml> "
;
}
//------------------------------
//处理业务完毕
//------------------------------
BufferedOutputStream out =
new
BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
else
{
logger.info(
"通知签名验证失败"
);
}
}
|
签名验证算法和签名生成的算法类似,在上面PayCommonUtil工具类中提供。 3、后话 。
感觉微信扫描支付体验效果还是挺好的,唯一缺点就是相关文档零散,官方的demo居然没有java编写的,希望之后微信官方能够逐步完善吧! 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
最后此篇关于JAVA微信扫码支付模式二线上支付功能实现以及回调的文章就讲到这里了,如果你想了解更多关于JAVA微信扫码支付模式二线上支付功能实现以及回调的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在哪里可以找到有关退款状态的信息? 对于“取消”和“失败”,没关系。但是“已捕获”、“已授权”、“暂停”和“待处理”之间有什么区别。支付宝是什么意思? 谢谢 最佳答案 以下是 Paypal 付款状态列
我想问问是否有人知道无需用户交互即可进行 PayPal 付款的可能性。我目前正在开展一个项目,我们希望在用户无需登录 PayPal 的情况下按需进行 PayPal 付款。 我发现的是: 做引用交易 定
我无法找到 PayPal 提供的关于它如何处理 webhook 以进行支付的文档。付款是否与付款一样对待,以便完成的付款将启动付款完成的 webhook?一般来说,付款和支出是否通过网络 Hook 相
我有一个表单,人们可以在其中以固定金额订购一件商品。以下是步骤: 客户填写表格 客户点击提交并进入评论页面,他可以在其中检查他的输入 在评论表单中应该有一个使用 paypal 支付的按钮(带有自己的设
用了微信sdk各种痛苦,感觉比qq sdk调用麻烦多了,回调过于麻烦,还必须要在指定包名下的actvity进行回调,所以我在这里写一篇博客,有这个需求的朋友可以借鉴一下,以后自己别的项目有用到也有个
这个问题已经有答案了: 已关闭10 年前。 Possible Duplicate: How to set Azure Max Spending Limit or Cost CAP $ amount?
我正在尝试将 Stripe 集成到 Symfony2 项目上,我在他们的文档中看到的唯一付款方式是“用卡付款”按钮 https://stripe.com/docs/checkout它在我的项目上创建了
这个问题已经有答案了: 已关闭10 年前。 Possible Duplicate: How to set Azure Max Spending Limit or Cost CAP $ amount?
我想使用 firebase 作为服务器并向 stripe 发送付款请求。我在服务器端的经验为零,所以我正在寻找一种简单的方法来实现这一目标。此帖Is it possible to integrate
我在这里使用自适应 Paypal 支付控制台:https://apigee.com/console/paypal向人们付款。我无法找到一种方法在不授予他们许可的情况下向个人汇款(来 self )。我希
我一直坚持使用 OpenCart 的 PayPal 标准付款,但尚未找到解决方案。 我做了什么: 在 sandbox.paypal.com 上创建了企业帐户 设置以下网络首选项: 自动返回(指定 UR
我想在我的 WPF 应用程序中通过 PayPal 进行购物付款。我想为用户生成一个链接,但不知道如何通知应用程序已接受付款。是否唯一的解决方案将是服务器,它会检查付款是否已被接受并且应用程序会不时轮询
PayPal PayOut REST API 没有记录它的局限性。什么是: 每次付款的最大收款人数? 每次付款的最高金额? 每个收件人的最大金额? 注意: 我查看了在线文档和 paypal 论坛 (
我是 web 开发的新手,我需要做的是创建一个表单,用户可以在其中输入一些东西到一个字段中,让我们暂时称之为名称......然后当他们去 paypal 时,他们会捐赠 1 美元(预定)然后从支付给 P
我正在尝试将 PayPal 集成到我的应用程序中并了解 PayPal SDK 的工作原理我正在使用示例应用程序。我知道在用户按下“购买”按钮后,我需要将授权响应发送到我的服务器以验证付款。问题是我找不
在我的 oscar 应用程序中,django 需要自适应并行 oscar 支付程序。 谁能告诉我如何将 Adaptive Parallel Paypal 方法集成到 Normal Paypal 中。我
您好,我正在开发用于 NFC 支付的安卓应用程序。 Android 设置中有一个选项可以使用打开的应用程序而不是默认应用程序。例如,当我将默认应用程序设置为 Android Pay 并在付款前打开我的
我构建了一个使用命名空间和 PSR-0 自动加载的 PHP 应用程序。在尝试实现 Stripe库,我发现它似乎无法加载类,因为它们没有命名空间。如果我手动包含文件,有没有办法不自动加载? // Get
有人成功地将 Adyen 支付插件集成到 iOS 应用程序中吗? 我到了它向用户显示付款选项的地步,但选择其中一个选项没有任何作用。我希望它会转到表单以捕获卡的详细信息,但事实并非如此。 如有任何帮助
我正在与 Stripe Payment Gateway 集成——API 的最新版本。 除了“client_reference_id”没有传递给 webhook 或事务(没有出现在日志中)之外,这一切都
我是一名优秀的程序员,十分优秀!