一、微信公众号支付APIJS:

要完整的实现微信支付功能,需要前后端一起实现,还需要微信商户平台的配置。这里只是涉及服务端的代码。

jar包:pom.xml

<!-- ↓↓↓↓↓↓↓↓ 支付相关 ↓↓↓↓↓↓↓↓ -->
<!-- http -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<!-- xml处理 -->
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.1.3</version>
</dependency>
<!-- fast-json包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.39</version>
</dependency>
<!-- ↑↑↑↑↑↑↑↑ 支付相关 ↑↑↑↑↑↑↑↑ -->

目录结构:

MD5Util.java

package webapp.payutil;

import java.security.MessageDigest;

public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) resultSb.append(byteToHexString(aB));
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 = 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 ignored) {
}
return resultString;
} private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

PayCommonUtil.java

package webapp.payutil;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder; import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.*; public class PayCommonUtil {
//微信参数配置
public static String API_KEY = "kshdf554sdf4s5f2sf121sdfsd1f5sdf";
public static String APPID = "wxd5609f7b5b4dd051";
public static String MCH_ID = "1230989902"; /**
* 微信公众号支付
* @param trade_no 订单号
* @param totalAmount 支付金额
* @param description 文字内容说明
* @param attach 自定义参数 length=127
* @param openId 微信公众号openId
* @param wxnotify 回调地址
* @param request -
* @return -
*/
public static SortedMap<String, Object> WxPublicPay(String trade_no, BigDecimal totalAmount, String description, String attach, String openId, String wxnotify, HttpServletRequest request) {
Map<String, String> map = weixinPrePay(trade_no,totalAmount,description,attach,openId,wxnotify,request);
SortedMap<String, Object> finalpackage = new TreeMap<>();
finalpackage.put("appId", PayCommonUtil.APPID);
finalpackage.put("timeStamp", System.currentTimeMillis() / 1000);
finalpackage.put("nonceStr", getRandomString(32));
finalpackage.put("package", "prepay_id=" + map.get("prepay_id"));
finalpackage.put("signType", "MD5");
String sign = PayCommonUtil.createSign(finalpackage);
finalpackage.put("paySign", sign);
return finalpackage;
} /**
* -
* @param trade_no 订单号
* @param totalAmount 支付金额
* @param description 文字内容说明
* @param attach 自定义参数 length=127
* @param openid 微信公众号openId
* @param wxnotify 回调地址
* @param request -
* @return -
*/
private static Map<String, String> weixinPrePay(String trade_no, BigDecimal totalAmount, String description, String attach, String openid, String wxnotify, HttpServletRequest request) {
SortedMap<String, Object> parameterMap = new TreeMap<>();
parameterMap.put("appid", PayCommonUtil.APPID);
parameterMap.put("mch_id", PayCommonUtil.MCH_ID);
parameterMap.put("nonce_str", getRandomString(32));
parameterMap.put("body", description);
parameterMap.put("attach", attach);
parameterMap.put("out_trade_no", trade_no);
parameterMap.put("fee_type", "CNY");
BigDecimal total = totalAmount.multiply(new BigDecimal(100));
java.text.DecimalFormat df = new java.text.DecimalFormat("0");
parameterMap.put("total_fee", df.format(total));
parameterMap.put("spbill_create_ip", request.getRemoteAddr());
parameterMap.put("notify_url", wxnotify);
parameterMap.put("trade_type", "JSAPI");
//trade_type为JSAPI是 openid为必填项
parameterMap.put("openid", openid);
String sign = PayCommonUtil.createSign(parameterMap);
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
String result = PayCommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
System.out.println(result);
Map<String, String> map = null;
try {
map = PayCommonUtil.doXMLParse(result);
} catch (JDOMException | IOException e) {
e.printStackTrace();
}
return map;
} //随机字符串生成
private static String getRandomString(int length) { //length表示生成字符串的长度
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
} //请求xml组装
private static String getRequestXml(SortedMap<String, Object> parameters){
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
Set es = parameters.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
sb.append("<").append(key).append(">").append("<![CDATA[").append(value).append("]]></").append(key).append(">");
} else {
sb.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
}
}
sb.append("</xml>");
return sb.toString();
} //生成签名
private static String createSign(SortedMap<String, Object> parameters){
StringBuilder sb = new StringBuilder();
Set es = parameters.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k).append("=").append(v).append("&");
}
}
sb.append("key=").append(API_KEY);
System.out.println(sb.toString());
return MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
} /**
* 验证回调签名
* @param map
* @return
*/
public static boolean isTenpaySign(Map<String, String> map) {
String charset = "utf-8";
String signFromAPIResponse = map.get("sign");
if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
//过滤空 设置 TreeMap
SortedMap<String,String> packageParams = new TreeMap<>();
for (String parameter : map.keySet()) {
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
StringBuilder sb = new StringBuilder();
Set es = packageParams.entrySet();
for (Object e : es) {
Map.Entry entry = (Map.Entry) e;
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!"sign".equals(k) && null != v && !"".equals(v)) {
sb.append(k).append("=").append(v).append("&");
}
}
sb.append("key=").append(API_KEY);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//算出签名
String tobesign = sb.toString();
String resultSign = MD5Util.MD5Encode(tobesign, "utf-8").toUpperCase();
String tenpaySign = packageParams.get("sign").toUpperCase();
return tenpaySign.equals(resultSign);
} //请求方法
private static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuilder buffer = new StringBuilder();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:{}"+ ce);
} catch (Exception e) {
System.out.println("https请求异常:{}"+ e);
}
return null;
} //退款的请求方法
public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
StringBuilder res = new StringBuilder("");
try (FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"))) {
keyStore.load(instream, "".toCharArray());
} // Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "1313329201".toCharArray())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
try (CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build()) {
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
StringEntity entity2 = new StringEntity(outputStr, Consts.UTF_8);
httpost.setEntity(entity2);
System.out.println("executing request" + httpost.getRequestLine());
try (CloseableHttpResponse response = httpclient.execute(httpost)) {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text = "";
res.append(text);
while ((text = bufferedReader.readLine()) != null) {
res.append(text);
System.out.println(text);
}
}
EntityUtils.consume(entity);
}
}
return res.toString(); } //xml解析
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();
for (Object aList : list) {
Element e = (Element) aList;
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
} private static String getChildrenText(List children) {
StringBuilder sb = new StringBuilder();
if(!children.isEmpty()) {
for (Object aChildren : children) {
Element e = (Element) aChildren;
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<").append(name).append(">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</").append(name).append(">");
}
} return sb.toString();
}
}

WeiXinPayController.java

package webapp.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.jdom.JDOMException;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import webapp.payutil.PayCommonUtil; import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Map;
import java.util.SortedMap;
import java.util.UUID; @Controller
@RequestMapping(value = "/api")
public class WeiXinPayController {
private static String wxnotify = "/api/json/money/wxpay/succ"; /**
* @param totalAmount 支付金额
* @param description 描述
* @param openId 微信公众号openId (可以前端传code,然后后台再通过微信对应接口换取openId)
* @param request -
* @return -
*/
@RequestMapping(value = "/weixin/weixinPay/{totalAmount}/{description}/{openId}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public SortedMap<String, Object> ToPay(@PathVariable BigDecimal totalAmount, @PathVariable String description, @PathVariable String openId, HttpServletRequest request) {
String sym = request.getRequestURL().toString().split("/api/")[0];
// 订单号
String tradeNo = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase().substring(0,32);
// 回调地址
String notifyUrl = sym + wxnotify;
// 自定义参数
Long userId = 100L; //对应用户id自己修改
JSONObject jsAtt = new JSONObject();
jsAtt.put("uid", userId);
String attach = jsAtt.toJSONString();
// 返回预支付参数
return PayCommonUtil.WxPublicPay(tradeNo, totalAmount, description, attach, openId, notifyUrl, request);
} /**
* 支付回调地址
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value = "/json/money/wxpay/succ",
produces = MediaType.APPLICATION_JSON_VALUE)
public String wxpaySucc(HttpServletRequest request) throws IOException {
System.out.println("微信支付回调");
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> params = null;
try {
params = PayCommonUtil.doXMLParse(resultxml);
} catch (JDOMException e) {
e.printStackTrace();
}
outSteam.close();
inStream.close();
if (!PayCommonUtil.isTenpaySign(params)) {
// 支付失败
return "fail";
} else {
System.out.println("===============付款成功==============");
// ------------------------------
// 处理业务开始
// ------------------------------
// 此处处理订单状态,结合自己的订单数据完成订单状态的更新
// ------------------------------ String total_fee = params.get("total_fee");
double v = Double.valueOf(total_fee) / 100;
// 取出用户id
String attach = params.get("attach");
JSONObject jsonObject = JSON.parseObject(attach);
Long userId = Long.parseLong(jsonObject.get("uid").toString()); //更新
//updateUserPay(userId, String.valueOf(v)); // 处理业务完毕
// ------------------------------
return "success";
}
} }

二、app支付:

和公众号支付一样,但是支付接口不需要openId,

PayCommonUtil.java:

第83行改为parameterMap.put("trade_type", "APP");

去掉第85行。

参考:http://blog.csdn.net/gbguanbo/article/details/50915333

注意项目中如果有权限配置,微信支付相关接口权限要放行。

一些资料:

JAVA微信扫码支付模式二功能实现以及回调:http://blog.csdn.net/bestlove12345/article/details/51858203

微信APP支付Java后端回调处理:  http://www.cnblogs.com/xu-xiang/p/5804757.html

回调:  http://blog.csdn.net/maple980326/article/details/51828554

微信支付(公众号支付APIJS、app支付)服务端统一下单接口java版的更多相关文章

  1. 微信公众号中的支付宝支付与微信支付 && 支付宝支付问题(微信bug)

    一般,在微信公众号中的商城都是需要支持微信支付和支付宝支付的,当然,较大的公司对于鹅厂和阿里的站队就不说了,所以这里简单记录一下支付宝支付和微信支付的主要流程.说是简单介绍,这是因为确实不难,因为前端 ...

  2. 微信JSAPI 公众号支付 H5支付以及APP支付 WEBAPI接口开发测试

    统一下单入口 调用该方法入口: public void WxPayAPI() { //string PayPrice ="99.9"; ////订单号 //string Payor ...

  3. 微信支付-公众号支付H5调用支付详解

    微信公众号支付 最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验. 一. ...

  4. asp.net mvc下实现微信公众号(JsApi)支付介绍

    本文主要讲解asp.net mvc框架下公众号支付如何实现,公众号支付主要包括三个核心代码,前台调起支付js代码.对应js调用参数参数生成代码.支付成功处理代码. 一.微信支付方式介绍 微信提供了各种 ...

  5. JAVA开发微信支付-公众号支付/微信浏览器支付(JSAPI)

    写这篇文章的目的有2个,一是自己的项目刚开发完微信支付功能,趁热回个炉温习一下,二也是帮助像我这样对微信支付不熟悉,反复看了多天文档还是一知半解,原理都没摸清,更不要说实现了.本以为网上的微信开发教程 ...

  6. 微信支付公众号支付redirect_uri域名与后台配置不一致,错误码10003

    最近弄微信支付,微信支付公众号支付redirect_uri域名与后台配置不一致,错误码10003,最容易出错两个地方 1,appid 对应不到 2,开发者网页授权 填写域名 文章来自http://ww ...

  7. H5页面获取openid,完成支付公众号(未关注公众号)支付

    一.页面授权 // 进入页面获取权限code function initAuthorizeCode() { var appid = $("#appid").val();//公众号a ...

  8. #PHP#微信支付 第二篇 JSAPI 调用统一下单接口获取预支付交易数据

    上一篇讲到成功获取 openid,本篇要调用微信统一接口创建预支付交易单,并获取到相关数据,以便(后边)在微信内调起H5支付 第三步,调用微信统一下单接口创建预支付交易单 微信统一下单API是微信支付 ...

  9. SpringBoot项目后台对接微信支付开发——微信统一下单接口开发

    开始没找到微信支付的sdk.自己根据官方给的接口文档纯手写,各种xml转JSON,JSON转xml,加密解密,签名....整个人都是崩溃的 开发的第三天,发现有官方的sdk.心情一下子豁然开朗,整个人 ...

随机推荐

  1. js控制表单操作的常用代码小结

    收集了一些在WEB前台开发中常用的一些控制表单操作函数. 1.鼠标经过时自动选择文本鼠标划过自动选中:<input type="text" value="默认值&q ...

  2. 有限狀態機FSM coding style整理 (SOC) (Verilog)

    AbstractFSM在數位電路中非常重要,藉由FSM,可以讓數位電路也能循序地執行起演算法.本文將詳細討論各種FSM coding style的優缺點,並歸納出推薦的coding style. In ...

  3. C++windows内核编程笔记day13 进程、线程与信号量

    Windows进程 进程是一个容器,包括程序运行须要的代码.数据.资源等信息, windows进程的特点: 每一个进程都有自己的ID号 每一个进程都有自己的地址空间.进程之间无法訪问对方的地址空间. ...

  4. 基于S3C2440的U-BOOT的start.S分析

    基于S3C2440的U-BOOT的start.S分析 在了解了ARM相关的汇编指令后,同时结合网上各位大虾的提点开始阅读u-boot的启动代码,现将分析过程记录如下 可执行文件及内存映射 我们可以把可 ...

  5. PowerShell中实现人机交互

    编写脚本的过程中有很多时候需要进行人机交互,比如我写一个脚本,需要动态的输入一些内容,比如用户名和密码之类的东西,这些是没办法事先写进代码里的.而通过外部文件进行信息读取,友好性又差了点.所以当我们需 ...

  6. c++多态之——vptr指针

    之前做过一个测试,在一个类中定义一个virtual修饰的函数时,sizeof这个类,发现类的大小多了恰好一个指针的字节大小,当初不明白,只是记住有这么一个特性.后来,发现它就是c++编译器给我们添加的 ...

  7. vcpkg —— VC++ 打包工具

    引用: http://www.tuicool.com/articles/aeiYz2v vcpkg 是微软 C++ 团队开发的在 Windows 上运行的 C/C++ 项目包管理工具,可以帮助您在 W ...

  8. ExtJs Ext.form.field.TextArea+Ckeditor 扩展富文本编辑器

    Ext.define("MyApp.base.BaseTextArea", { extend: "Ext.form.field.TextArea", xtype ...

  9. Oracle数据误删除的恢复操作

    flashbackup 闪回操作: 1. 打开表的闪回功能: alter table dw_stg.fm_user_play_d enable row movement; 2. 查询要闪回的表的记录信 ...

  10. Java GUI画圆。

    package ydj; import java.awt.*; import javax.swing.*; public class huayuan extends JFrame { public h ...