java union pay 代码_微信公众号支付【Java版】

微信公众号支付【Java版】

说明:

① 本文主要讲解的是微信公众号内(商城)支付部分,如需了解其他微信公众号开发内容,请访问:http://blog.csdn.net/lyq8479/article/details/8944988【柳峰的博客 内容为2013year的有些内容已经改变,如需交流,请留言】

③ 本文由个人的印象笔记导出,如你也在使用印象笔记,我可以直接把笔记分享给你,我的印象笔记:dabingryan@gmail.com

喜欢的小伙伴欢迎关注我的公众号:Java实战。

1.准备工作

首先登录微信公众平台,获取并配置以下微信开发配置:

开发者ID【AppID和AppSecret】

服务器配置

1.url服务器地址设置

2.Token【自己设置,必须英文或数字】

3.EncodingAESKey[自己随机生成,用于消息加解密]

然后登录微信商户平台,获取并配置以下微信支付配置:

商户号(mchId)

API秘钥(key)

API证书(java版主要使用:apiclient_cert.p12)

2.代码展示

提醒:此处粘贴出的代码为方便初学者比较直观的了解、学习微信公众号支付,部分代码并未按照编码规范封装成方法、工具类

将微信支付所有参数定义为 WeChatConfig.java

public class WeChatConfig {

/**公众号AppId*/

public static final APP_ID = "";

/**公众号AppSecret*/

public static final APP_SECRET = "";

/**微信支付商户号*/

public static final String MCH_ID = "";

/**微信支付API秘钥*/

public static final String KEY = "";

/**微信支付api证书路径*/

public static final String CERT_PATH = "***/apiclient_cert.p12";

/**微信统一下单url*/

public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

/**微信申请退款url*/

public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

/**微信支付通知url*/

public static final String NOTIFY_URL = "此处url用于接收微信服务器发送的支付通知,并处理商家的业务";

/**微信交易类型:公众号支付*/

public static final String TRADE_TYPE_JSAPI = "JSAPI";

/**微信交易类型:原生扫码支付*/

public static final String TRADE_TYPE_NATIVE = "NATIVE";

/**微信甲乙类型:APP支付*/

public static final String TRADE_TYPE_APP = "APP";

}

处理微信公众号支付请求的Controller:WeChatOrderController.java

@RequestMapping(value="/m/weChat/")

@Controller("weChatOrderController")

public class WeChatOrderController{

@Autowired

private OrderService orderService;

@Autowired

private WechatPayService wechatPayService;

@Autowired

private NotifyReturnService notifyReturnService;

@RequestMapping(value = "unifiedOrder")

public String unifiedOrder(HttpServletRequest request,Model model){

//用户同意授权,获得的code

String code = request.getParameter("code");

//请求授权携带的参数【根据自己需要设定值,此处我传的是订单id】

String state = request.getParameter("state");

Order order = orderService.get(state);//订单信息

//通过code获取网页授权access_token

AuthToken authToken = WeChatUtils.getTokenByAuthCode(code);

//构建微信统一下单需要的参数

Map map = Maps.newHashMap();

map.put("openId",authToken.getOpenid());//用户标识openId

map.put("remoteIp",request.getRemoteAddr());//请求Ip地址

//调用统一下单service

Map resultMap = WeChatPayService.unifiedOrder(order,map);

String returnCode = (String) resultMap.get("return_code");//通信标识

String resultCode = (String) resultMap.get("result_code");//交易标识

//只有当returnCode与resultCode均返回“success”,才代表微信支付统一下单成功

if (WeChatConstant.RETURN_SUCCESS.equals(resultCode)&&WeChatConstant.RETURN_SUCCESS.equals(returnCode)){

String appId = (String) resultMap.get("appid");//微信公众号AppId

String timeStamp = WeChatUtils.getTimeStamp();//当前时间戳

String prepayId = "prepay_id="+resultMap.get("prepay_id");//统一下单返回的预支付id

String nonceStr = WeChatUtils.getRandomStr(20);//不长于32位的随机字符串

SortedMap signMap = Maps.newTreeMap();//自然升序map

signMap.put("appId",appId);

signMap.put("package",prepayId);

signMap.put("timeStamp",timeStamp);

signMap.put("nonceStr",nonceStr);

signMap.put("signType","MD5");

model.addAttribute("appId",appId);

model.addAttribute("timeStamp",timeStamp);

model.addAttribute("nonceStr",nonceStr);

model.addAttribute("prepayId",prepayId);

model.addAttribute("paySign",WeChatUtils.getSign(signMap));//获取签名

}else {

logger.error("微信统一下单失败,订单编号:"+order.getOrderNumber()+",失败原因:"+resultMap.get("err_code_des"));

return "redirect:/m/orderList";//支付下单失败,重定向至订单列表

}

//将支付需要参数返回至页面,采用h5方式调用支付接口

return "/mobile/order/h5Pay";

}

}

微信支付前端发起页面: weChatPayTest.jsp

支付按钮href中的redirect_uri= http://自己服务的ip或者域名/m/weChat/unifiedOrder 强调部分需要进行uriEncode

此处代码为在微信公众号内网页调用,故使用的是微信网页授权方式,将订单id通过支付接口中state参数进行传递

微信公众号支付测试

h5方式调用微信支付接口:h5Pay.jsp

WeixinJSBridge为微信公众号内置对象,所以必须在公众号内部网页使用

确认支付

function onBridgeReady(){

var appId = $("input[name='appId']").val();

var nonceStr = $("input[name='nonceStr']").val();

var prepayId = $("input[name='prepayId']").val();

var paySign = $("input[name='paySign']").val();

var timeStamp = $("input[name='timeStamp']").val();

WeixinJSBridge.invoke(

'getBrandWCPayRequest', {

"appId":appId,

"timeStamp":timeStamp,

"nonceStr":nonceStr,

"package":prepayId,

"signType":"MD5",

"paySign":paySign

},

function(res){

if(res.err_msg == "get_brand_wcpay_request:ok" ) {

location.href="支付成功返回商家自定义页面";

}else {//这里支付失败和支付取消统一处理

alert("支付取消");

location.href="支付失败返回商家自定义页面";

}

}

);

}

$(document).ready(function () {

if (typeof WeixinJSBridge == "undefined"){

if (document.addEventListener){

document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);

}else if (document.attachEvent){

document.attachEvent('WeixinJSBridgeReady', onBridgeReady);

document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);

}

}else {

onBridgeReady();

}

});

微信支付订单Service:WeChatPayService.java

/**

*微信支付统一下单

**/

public Map unifiedOrder(Order order, Map map){

Map resultMap;

try {

WxPaySendData paySendData = new WxPaySendData();

//构建微信支付请求参数集合

paySendData.setAppId(WeChatConstant.APP_ID);

paySendData.setAttach("微信订单支付:"+order.getOrderNumber());

paySendData.setBody("商品描述");

paySendData.setMchId(WeChatConfig.MCH_ID);

paySendData.setNonceStr(WeChatUtils.getRandomStr(32));

paySendData.setNotifyUrl(WeChatConfig.NOTIFY_URL);

paySendData.setDeviceInfo("WEB");

paySendData.setOutTradeNo(order.getOrderNumber());

paySendData.setTotalFee(order.getSumFee());

paySendData.setTradeType(WeChatConfig.TRADE_TYPE_JSAPI);

paySendData.setSpBillCreateIp((String) map.get("remoteIp"));

paySendData.setOpenId((String) map.get("openId"));

//将参数拼成map,生产签名

SortedMap params = buildParamMap(paySendData);

paySendData.setSign(WeChatUtils.getSign(params));

//将请求参数对象转换成xml

String reqXml = WeChatUtils.sendDataToXml(paySendData);

//发送请求

byte[] xmlData = reqXml.getBytes();

URL url = new URL(WeChatConfig.UNIFIED_ORDER_URL);

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

urlConnection.setDoOutput(true);

urlConnection.setDoInput(true);

urlConnection.setUseCaches(false);

urlConnection.setRequestProperty("Content_Type","text/xml");

urlConnection.setRequestProperty("Content-length",String.valueOf(xmlData.length));

DataOutputStream outputStream = new DataOutputStream(urlConnection.getOutputStream());

outputStream.write(xmlData);

outputStream.flush();

outputStream.close();

resultMap = WeChatUtils.parseXml(urlConnection.getInputStream());

} catch (Exception e) {

throw new ServiceException("微信支付统一下单异常",e);

}

return resultMap;

/**

* 构建统一下单参数map 用于生成签名

* @param data

* @return SortedMap

*/

private SortedMap buildParamMap(WxPaySendData data) {

SortedMap paramters = new TreeMap();

if (null != data){

if (StringUtils.isNotEmpty(data.getAppId())){

paramters.put("appid",data.getAppId());

}

if (StringUtils.isNotEmpty(data.getAttach())){

paramters.put("attach",data.getAttach());

}

if (StringUtils.isNotEmpty(data.getBody())){

paramters.put("body",data.getBody());

}

if (StringUtils.isNotEmpty(data.getDetail())){

paramters.put("detail",data.getDetail());

}

if (StringUtils.isNotEmpty(data.getDeviceInfo())){

paramters.put("device_info",data.getDeviceInfo());

}

if (StringUtils.isNotEmpty(data.getFeeType())){

paramters.put("fee_type",data.getFeeType());

}

if (StringUtils.isNotEmpty(data.getGoodsTag())){

paramters.put("goods_tag",data.getGoodsTag());

}

if (StringUtils.isNotEmpty(data.getLimitPay())){

paramters.put("limit_pay",data.getLimitPay());

}

if (StringUtils.isNotEmpty(data.getMchId())){

paramters.put("mch_id",data.getMchId());

}

if (StringUtils.isNotEmpty(data.getNonceStr())){

paramters.put("nonce_str",data.getNonceStr());

}

if (StringUtils.isNotEmpty(data.getNotifyUrl())){

paramters.put("notify_url",data.getNotifyUrl());

}

if (StringUtils.isNotEmpty(data.getOpenId())){

paramters.put("openid",data.getOpenId());

}

if (StringUtils.isNotEmpty(data.getOutTradeNo())){

paramters.put("out_trade_no",data.getOutTradeNo());

}

if (StringUtils.isNotEmpty(data.getSign())){

paramters.put("sign",data.getSign());

}

if (StringUtils.isNotEmpty(data.getSpBillCreateIp())){

paramters.put("spbill_create_ip",data.getSpBillCreateIp());

}

if (StringUtils.isNotEmpty(data.getTimeStart())){

paramters.put("time_start",data.getTimeStart());

}

if (StringUtils.isNotEmpty(data.getTimeExpire())){

paramters.put("time_expire",data.getTimeExpire());

}

if (StringUtils.isNotEmpty(data.getProductId())){

paramters.put("product_id",data.getProductId());

}

if (data.getTotalFee()>0){

paramters.put("total_fee",data.getTotalFee());

}

if (StringUtils.isNotEmpty(data.getTradeType())){

paramters.put("trade_type",data.getTradeType());

}

//申请退款参数

if (StringUtils.isNotEmpty(data.getTransactionId())){

paramters.put("transaction_id",data.getTransactionId());

}

if (StringUtils.isNotEmpty(data.getOutRefundNo())){

paramters.put("out_refund_no",data.getOutRefundNo());

}

if (StringUtils.isNotEmpty(data.getOpUserId())){

paramters.put("op_user_id",data.getOpUserId());

}

if (StringUtils.isNotEmpty(data.getRefundFeeType())){

paramters.put("refund_fee_type",data.getRefundFeeType());

}

if (null != data.getRefundFee() && data.getRefundFee()>0){

paramters.put("refund_fee",data.getRefundFee());

}

}

return paramters;

}

}

微信工具类 WeChatUtils.java

public class WeChatUtils {

/**

* 根据code获取微信授权access_token

* @param request

*/

public static AuthToken getTokenByAuthCode(String code){

AuthToken authToken;

StringBuilder json = new StringBuilder();

try {

URL url = new URL(WeChatConstant.GET_AUTHTOKEN_URL+"appid="+ WeChatConstant.APP_ID+"&secret="+ WeChatConstant.APP_SECRET+"&code="+code+"&grant_type=authorization_code");

URLConnection uc = url.openConnection();

BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));

String inputLine ;

while((inputLine=in.readLine())!=null){

json.append(inputLine);

}

in.close();

//将json字符串转成javaBean

ObjectMapper om = new ObjectMapper();

authToken = readValue(json.toString(),AuthToken.class);

} catch (Exception e) {

throw new ServiceException("微信工具类:根据授权code获取access_token异常",e);

}

return authToken;

}

/**

* 获取微信签名

* @param map 请求参数集合

* @return 微信请求签名串

*/

public static String getSign(SortedMap map){

StringBuffer sb = new StringBuffer();

Set set = map.entrySet();

Iterator iterator = set.iterator();

while (iterator.hasNext()){

Map.Entry entry = (Map.Entry) iterator.next();

String k = (String) entry.getKey();

Object v = entry.getValue();

//参数中sign、key不参与签名加密

if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){

sb.append(k + "=" + v + "&");

}

}

sb.append("key=" + WeChatPayConfig.KEY);

String sign = MD5.MD5Encode(sb.toString()).toUpperCase();

return sign;

}

/**

* 解析微信服务器发来的请求

* @param inputStream

* @return 微信返回的参数集合

*/

public static SortedMap parseXml(InputStream inputStream) {

SortedMap map = Maps.newTreeMap();

try {

//获取request输入流

SAXReader reader = new SAXReader();

Document document = reader.read(inputStream);

//得到xml根元素

Element root = document.getRootElement();

//得到根元素所有节点

List elementList = root.elements();

//遍历所有子节点

for (Element element:elementList){

map.put(element.getName(),element.getText());

}

//释放资源

inputStream.close();

} catch (Exception e) {

throw new ServiceException("微信工具类:解析xml异常",e);

}

return map;

}

/**

* 扩展xstream,使其支持name带有"_"的节点

*/

public static XStream xStream = new XStream(new DomDriver("UTF-8",new XmlFriendlyNameCoder("-_","_")));

/**

* 请求参数转换成xml

* @param data

* @return xml字符串

*/

public static String sendDataToXml(WxPaySendData data){

xStream.autodetectAnnotations(true);

xStream.alias("xml", WxPaySendData.class);

return xStream.toXML(data);

}

/**

* 获取当前时间戳

* @return 当前时间戳字符串

*/

public static String getTimeStamp(){

return String.valueOf(System.currentTimeMillis()/1000);

}

/**

* 获取指定长度的随机字符串

* @param length

* @return 随机字符串

*/

public static String getRandomStr(int length){

String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

Random random = new Random();

StringBuffer sb = new StringBuffer();

for (int i = 0; i < length; i++) {

int number = random.nextInt(base.length());

sb.append(base.charAt(number));

}

return sb.toString();

}

}

微信常量类 WeChatConstant.java

public class WeChatConstant {

/**Token*/

public static final String TOKEN = "";

/**EncodingAESKey*/

public static final String AES_KEY = "";

/**消息类型:文本消息*/

public static final String MESSAGE_TYPE_TEXT = "text";

/**消息类型:音乐*/

public static final String MESSAGE_TYPE_MUSIC = "music";

/**消息类型:图文*/

public static final String MESSAGE_TYPE_NEWS = "news";

/**消息类型:图片*/

public static final String MESSAGE_TYPE_IMAGE = "image";

/**消息类型:视频*/

public static final String MESSAGE_TYPE_VIDEO = "video";

/**消息类型:小视频*/

public static final String MESSAGE_TYPE_SHORTVIDEO = "shortvideo";

/**消息类型:链接*/

public static final String MESSAGE_TYPE_LINK = "link";

/**消息类型:地理位置*/

public static final String MESSAGE_TYPE_LOCATION = "location";

/**消息类型:音频*/

public static final String MESSAGE_TYPE_VOICE = "voice";

/**消息类型:事件推送*/

public static final String MESSAGE_TYPE_EVENT = "event";

/**事件类型:subscribe(订阅)*/

public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

/**事件类型:unsubscribe(取消订阅)*/

public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

/**事件类型:CLICK(自定义菜单点击事件)*/

public static final String EVENT_TYPE_CLICK = "CLICK";

/**返回消息类型:转发客服*/

public static final String TRANSFER_CUSTOMER_SERVICE="transfer_customer_service";

/**ACCESS_TOKEN*/

public static final String ACCESS_TOKEN_ENAME = "access_token";

/**返回成功字符串*/

public static final String RETURN_SUCCESS = "SUCCESS";

/**主动发送消息url*/

public static final String SEND_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=";

/**通过code获取授权access_token的URL*/

public static final String GET_AUTHTOKEN_URL = " https://api.weixin.qq.com/sns/oauth2/access_token?";

}

其他微信对象:

封装微信授权返回的信息,此处属性均为小写【微信返回的是小写很不友好】

public class AuthToken implements Serializable {

/**授权access_token*/

private String access_token;

/**有效期*/

private String expires_in;

/**刷新access_token*/

private String refresh_token;

/**用户OPENID*/

private String openid;

/**授权方式Scope*/

private String scope;

/**错误码*/

private String errcode;

/**错误消息*/

private String errmsg;

/**getter() and setter()*/

}

微信请求参数对象【下单与退款均可使用此对象】

public class WxPaySendData {

/**公众账号ID 必须*/

@XStreamAlias("appid")

private String appId;

/**商户号 必须*/

@XStreamAlias("mch_id")

private String mchId;

/**设备号*/

@XStreamAlias("device_info")

private String deviceInfo;

/**随机字符串 必须*/

@XStreamAlias("nonce_str")

private String nonceStr;

/**签名 必须*/

@XStreamAlias("sign")

private String sign;

/**商品描述 必须*/

@XStreamAlias("body")

private String body;

/**商品详情*/

@XStreamAlias("detail")

private String detail;

/**附加数据*/

@XStreamAlias("attach")

private String attach;

/**商户订单号 必须*/

@XStreamAlias("out_trade_no")

private String outTradeNo;

/**货币类型*/

@XStreamAlias("fee_type")

private String feeType;

/**交易金额 必须[JSAPI,NATIVE,APP]*/

@XStreamAlias("total_fee")

private int totalFee;

/**交易类型 [必须]*/

@XStreamAlias("trade_type")

private String tradeType;

/**通知地址 [必须]*/

@XStreamAlias("notify_url")

private String notifyUrl;

/**终端Ip [必须]*/

@XStreamAlias("spbill_create_ip")

private String spBillCreateIp;

/**订单生成时间yyyyMMddHHmmss*/

@XStreamAlias("time_start")

private String timeStart;

/**订单失效时间yyyyMMddHHmmss 间隔>5min*/

@XStreamAlias("time_expire")

private String timeExpire;

/**用户标识 tradeType=JSAPI时必须*/

@XStreamAlias("openid")

private String openId;

/**商品标记*/

@XStreamAlias("goods_tag")

private String goodsTag;

/**商品ID tradeType=NATIVE时必须*/

@XStreamAlias("product_id")

private String productId;

/**指定支付方式*/

@XStreamAlias("limit_pay")

private String limitPay;

/**

*以下属性为申请退款参数

*/

/**微信订单号 [商户订单号二选一]*/

@XStreamAlias("transaction_id")

private String transactionId;

/**商户退款单号 [必须]*/

@XStreamAlias("out_refund_no")

private String outRefundNo;

/**退款金额 [必须]*/

@XStreamAlias("refund_fee")

private Integer refundFee;

/**货币种类*/

@XStreamAlias("refund_fee_type")

private String refundFeeType;

/**操作员账号:默认为商户号 [必须]*/

@XStreamAlias("op_user_id")

private String opUserId;

/**getter() and setter()*/

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值