您的位置:首页 > 移动开发 > 微信开发

微信线下门店二维码扫码支付和退款

2016-05-31 15:46 405 查看
查看微信官网开发接口

微信线下门店扫码支付开发

流程:生成一个预付单-》生成二维码》付款交易成功(微信端接收到钱已付款,这时需要告诉商户系统我已收到钱,发送异步通知给商户系统,一般会发送多次有时间间隔。

商户系统的异步通知方法:处理自己系统的业务,一般修改交易流水状态,发送微信客服消息等等。

最后发送个“SUCCESS”内容的xml给微信端,这时微信端就不会再异步请求了。

1.生成预付订单

2.js生成二维码

3.回调通知方法  

4.微信退款

1.jsp页面的生成二维码

//*************start*******wxPayByQr********************************
function wxQrClick(){
var total_amount=$("#paymentAmount").val();
var orderId=$("#myorderId").val();
//check
var paySize=$(".payMoneyC").length;
var ptypeNum=$(".ptype:checked").length;
var paySum=0;
if(ptypeNum<=0){
layer.msg("支付方式至少选择一个!");
return;
}
if(ptypeNum!=1){
layer.msg("支付方式只能是微信扫码支付!");
return;
}
var companyId=$("#companyId").val();
var subCompanyId=$("#subCompanyId").val();
if(subCompanyId==null||subCompanyId=='null'||subCompanyId==undefined){
subCompanyId="";
}
$.ajax({
url : '<%=basePath%>/payPrepareByQr.action',
async : false,
type : "post",
dataType : "json",
data:{"orderAmount":total_amount,"orderId":orderId},
success : function(result) {
if(result.code=='SUCCESS'){
$("#wxQrBtn").hide();
$("#outputWXQr").show();
<span style="color:#ff0000;">jQuery('#outputWXQr').qrcode({width:200,height:200,text:result.code_url});</span>
window.setInterval(finshWXQrPay, 8000);
}else{
layer.msg("微信二维码生成出错!");
}
}
});
}

function finshWXQrPay(){
var orderId=$("#myorderId").val();
$.ajax({
url : '<%=basePath%>/aliPay!notifyFinshedByWXQr.action?orderId='+orderId,
async : false,
type : "post",
dataType : "json",
data:$('#finishForm').serialize(),
success : function(result) {
if(result.code!='0'){
layer.msg("微信扫码支付交易成功,订单3秒后即将关闭!");
window.setTimeout(function(){
//关闭弹出窗之前,跳转到其他页面
parent.window.location.href="<%=path%>/"+result.redirectUrl;
closeLayerDialog();
},3000);
}
}
});
}

//*************end*******wxPayByQr********************************


2.二维码调用的方法

/**
* 微信二维码扫码支付生成预支付交易单,并返回交易会话的二维码链接code_url
* @return
* @throws JDOMException
* @throws IOException
* @throws NumberFormatException
* @throws SQLException
*/
public String payPrepareByQr() throws JDOMException, IOException, NumberFormatException, SQLException {
Order order = orderService.getOrderById(Integer.parseInt(orderId));
CompanyPay cp = orderService.getCompanyPay(order.getCompanyId());
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
PrintWriter out = null;
if(order.getDealSts()==5||order.getDealSts()==7){
out.print("0");
return null;
}
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
userwxId = (userwxId==null||"".equals(userwxId))?order.getOrderPersonWXId():userwxId;
if (!StringUtils.isNotBlank(userwxId)) {
userwxId="";
}
if (!StringUtils.isNotBlank(ticketId)) {
ticketId="";
}
parameters.put("appid", cp.getAppId());
parameters.put("mch_id", cp.getMchId());
parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());// 随机字符串,不长于32位
parameters.put("body", order.getIsTakeOut()==0?"堂食订单":"外卖订单");//商品描述
String tradeNo = getTradeNo();
parameters.put("out_trade_no", tradeNo);//商户系统内部的订单号,32个字符内、可包含字母
float amount_f = Float.parseFloat(orderAmount);
Long amount = (long)(amount_f*100);
parameters.put("total_fee", amount.toString());//订单总金额,单位为分,不能带小数点
parameters.put("spbill_create_ip", request.getRemoteAddr());//订单生成的机器IP,第一个参数订单编号,第二个参数交易金额,第四个参数优惠劵编号
parameters.put("notify_url", ConfigUtil.WXQR_NOTIFY_URL+"?orderId="+orderId+","+orderAmount+","+tradeNo+","+ticketId);//接收微信支付成功通知
parameters.put("trade_type", "NATIVE");//JSAPI、NATIVE、APP

String sign = PayCommonUtil.createSign("UTF-8", parameters,cp.getApiKey());
parameters.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameters);
System.out.println("requestXML_------------------->>>>>:"+requestXML);
String result = CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL,"POST", requestXML);
Map<String, String> map = XMLUtil.doXMLParse(result);

SortedMap<Object, Object> params = new TreeMap<Object, Object>();
String return_code=(String)map.get("return_code");
String result_code=(String)map.get("result_code");
String json = "";
if("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){
params.put("code", map.get("return_code"));
params.put("appId", cp.getAppId());
params.put("prepay_id", map.get("prepay_id"));
params.put("trade_type", map.get("trade_type"));
params.put("code_url", map.get("code_url"));//trade_type为NATIVE是有返回,可将该参数值生成二维码展示出来进行扫码支付
json = JSONObject.fromObject(params).toString();
//生成交易流水,等回调后再改变状态
WXPayLog vo = new WXPayLog();
vo.setOutTradeNo(tradeNo);
vo.setOrderId(orderId);
vo.setTotalFee(amount_f);
vo.setPayOpenId(order.getOrderPersonWXId());
vo.setAppId(parameters.get("appid").toString());
vo.setStatus(0);
vo.setTradeDate(new Date());
vo.setTradeType("NATIVE");
orderService.insertWXPaylog(vo);
}else{
params.put("code", map.get("return_code"));
params.put("msg", map.get("return_msg"));
json = JSONObject.fromObject(params).toString();
}
System.out.println("微信扫码支付返回信息:="+json);
ResponseWriteUtil.writeHTML(json);
return null;
}

3.回调的方法

/**
* 微信扫码支付异步通知回调方法
* @return
* @throws Exception
*/
public String paySuccessByWXQr() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
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);
}
outSteam.close();
inStream.close();
System.out.println("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~~~~~~~~~~`");
List<String> tktIds = new ArrayList<String>();
//tktIds.add(ticketId);
//orderService.payNotify(orderId,tktIds,orderAmount);
String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
Map<Object, Object> map = XMLUtil.doXMLParse(result);
String paras = "";
String userwxId="";
if (map != null) {
for (Object keyValue : map.keySet()) {
//				System.out.println("[" + keyValue + "=" + map.get(keyValue)
//						+ "]");
if ("orderId".equals(keyValue)) {
paras = map.get(keyValue).toString();
}
if("openid".equals(keyValue)){
userwxId=map.get(keyValue).toString();
}
}
}
String[] para = paras.split(",");
String orderId=para[0];
String orderAmount=para[1];
String tradeNo=para[2];
String ticketId="";
if(para.length>3){
ticketId=para[3];
if(ticketId!=null && !"".equals(ticketId)){
tktIds.add(ticketId);
}
}
System.out.println("params="+orderId+","+orderAmount+","+tradeNo+","+userwxId +","+ticketId);
//判断付款是否成功,已成功则不再记录付款信息
if(orderService.ifOrderPaid(Integer.parseInt(orderId))>0){
response.getWriter().write(PayCommonUtil.setXML("SUCCESS", ""));
return null;
}
//更新交易流水状态
orderService.updateWXPayLogStatus(tradeNo);
// 增加付款信息
Order o = orderService.getOrderById(Integer.parseInt(orderId));
List<SysUser> us = sysUserService.fetchSysUserByOpenId(userwxId, o.getCompanyId());
SysUser u = (us!=null && us.size()>0)?us.get(0):null;
if(o.getIsTakeOut()==0){//堂吃
orderService.payNotify(orderId,tktIds,orderAmount,0);
}else if(o.getIsTakeOut()==1){//外卖
o.setPaymentAmount(Float.parseFloat(orderAmount));
o.setPaymentType(EnumUtil.PAYMENT_TYPE.weixin.getCode());//配送员结束订单,赞为支付方式为现金或刷卡两种方式
o.setPaymentTime(new Date());
o.setDealSts(EnumUtil.ORDER_dealSts.paid.getCode());
o.setFinishTime(new Date());
o.setOrderRemark("用户微信支付");

List<OrderPlus> ops = new ArrayList<OrderPlus>();
OrderPlus op = new OrderPlus();
op.setOrderId(o.getId());
op.setOrderAmount(o.getAmount());
op.setPaymentAmount(o.getPaymentAmount());
//op.setPaymentTime(new Date());
op.setActUser(u==null?"":u.getId());
op.setPaymentType(EnumUtil.PAYMENT_TYPE.weixin.getCode());
op.setNotes("用户微信支付:支付金额为"+o.getPaymentAmount());
ops.add(op);
orderService.taeoutOrderPaymet4admin(o, ops,tktIds);
}
orderService.updateOrderItemActualPrice(o.getId());
System.out.println("~~~~~~~~~~~~~~~~业务处理完成~~~~~~~~~~~~~~~~~~`");
//--------------------------消息发送成功-----------------------------------------end
if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
response.getWriter().write(PayCommonUtil.setXML("SUCCESS", "")); // 告诉微信服务器,我收到信息了,不要在调用回调action了
System.out.println("-------------"+ PayCommonUtil.setXML("SUCCESS", ""));
}
return null;
}


XMLUtil.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

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();
}

}

4.微信退款:首先需要有微信退款的证书,放到商户系统的目录下,然后调用微信退款接口

/**
* 微信--申请退款
*
* @param orderId
*            订单编号
* @param refundMoney
*            退款金额
* @param totalFee
*            订单总金额
* @param outRefundNo
*            商户退款单号
* @return
*/
public Map<String, Object> payRefund(String orderId, float refundMoney,
float totalFee, String outRefundNo) {
Map<String, Object> resultMap = new HashMap<String, Object>();
try {
Order order = orderService.getOrderById(Integer.parseInt(orderId));
CompanyPay cp = orderService.getCompanyPay(order.getCompanyId());
String tradeNo = orderRefundService
.selectOutTradeNoByOrderId(Integer.parseInt(orderId));
SortedMap<Object, Object> parameters = new TreeMap<Object, Object
d5bb
>();
parameters.put("appid", cp.getAppId());// 微信分配的公众账号ID
parameters.put("mch_id", cp.getMchId());// 微信支付分配的商户号
parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());// 随机字符串,不长于32位
// CHSGOFBZJ520150918162021614
parameters.put("out_trade_no", tradeNo);// 商户系统内部的订单号,32个字符内、可包含字母
parameters.put("out_refund_no", outRefundNo);// 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
Long refundMoney_f = (long) (refundMoney * 100);
Long totalFee_f = (long) (totalFee * 100);
parameters.put("total_fee", totalFee_f.toString());// 订单总金额,单位为分,不能带小数点
parameters.put("refund_fee", refundMoney_f.toString());// 退款总金额,单位为分,不能带小数点
parameters.put("op_user_id", cp.getMchId());// 操作员帐号, 默认为商户号
String sign = PayCommonUtil.createSign("UTF-8", parameters,
cp.getApiKey());
parameters.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameters);
System.out.println("requestXML_------------------->>>>>:"
+ requestXML);
// String result = CommonUtil.httpsRequest(ConfigUtil.REFUND_URL,
// "POST", requestXML);
Map<String, String> map = clientCustomSSLCall(
ConfigUtil.REFUND_URL, order.getCompanyId(), cp.getMchId(),
requestXML);
// log.debug("result:=" + result);
// Map<String, String> map = XMLUtil.doXMLParse(result);
if ("FAIL".equals(map.get("return_code"))) {
resultMap.put("result_flag", "fail");
resultMap.put("return_code", map.get("return_code"));
resultMap.put("return_msg", map.get("return_msg"));
} else if ("SUCCESS".equals(map.get("return_code"))) {
if ("SUCCESS".equals(map.get("result_code"))) {
resultMap.put("result_flag", "success");// SUCCESS退款申请接收成功,结果通过退款查询接口查询
resultMap.put("return_code", map.get("return_code"));
resultMap.put("return_msg", map.get("return_msg"));
resultMap.put("err_code", map.get("err_code"));
resultMap.put("err_code_des", map.get("err_code_des"));
} else if ("FAIL".equals(map.get("result_code"))) {
resultMap.put("result_flag", "fail");// FAIL 提交业务失败
resultMap.put("err_code", map.get("err_code"));
resultMap.put("err_code_des", map.get("err_code_des"));
}
}
} catch (Exception e) {
System.out.println("payRefund Exception:" + e.getMessage());
}
return resultMap;
}

/**
* 自定义SSL双向证书验证
*
* @param url
* @param mchId
* @param arrayToXml
* @return
* @throws Exception
*/
public Map<String, String> clientCustomSSLCall(String url,
String companyId, String mchId, String arrayToXml) throws Exception {
Map<String, String> doXMLtoMap = new HashMap<String, String>();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
String cAPath = ServletActionContext.getServletContext().getRealPath(
"/WEB-INF/ca/" + companyId + "/apiclient_cert.p12");
// System.out.println("capath:=" + cAPath);
FileInputStream instream = new FileInputStream(new File(cAPath));
try {
keyStore.load(instream, mchId.toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, mchId.toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf).build();
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(arrayToXml, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpPost);

String jsonStr = EntityUtils
.toString(response.getEntity(), "UTF-8");
doXMLtoMap = XMLUtil.doXMLParse(jsonStr);
log.debug("result jsonStr:=" + jsonStr);
response.close();
} finally {
httpclient.close();
}
return doXMLtoMap;
}

/**
* 微信退款证书是否存在
*
* @return
*/
public String isWxCAExist() {
String companyId = getCompanyInfo().getCompanyId();
String cAPath = ServletActionContext.getServletContext().getRealPath(
"/WEB-INF/ca/" + companyId + "/apiclient_cert.p12");
File f = new File(cAPath);
if (f.exists()) {
ResponseWriteUtil.writeHTML("{\"isExist\":\"1\"}");// 存在
} else {
ResponseWriteUtil.writeHTML("{\"isExist\":\"0\"}");// 不存在
}
return null;
}

PayCommonUtil.java

import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;

import org.apache.log4j.Logger;

public class PayCommonUtil {
private static Logger log = Logger.getLogger(PayCommonUtil.class);
public static String CreateNoncestr(int length) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < length; i++) {
Random rd = new Random();
res += chars.indexOf(rd.nextInt(chars.length() - 1));
}
return res;
}

public static String CreateNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
}
/**
* @Description:sign签名
* @param characterEncoding 编码格式
* @param parameters 请求参数
* @return
* @throws UnsupportedEncodingException
*/
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters,String apiKey) throws UnsupportedEncodingException{
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
//sb.append("key=" + ConfigUtil.API_KEY);
sb.append("key=" + apiKey);
System.out.println("createSign-----befor_md5_sign:"+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

public static String createSign4pay(String characterEncoding,SortedMap<Object,Object> parameters,String apiKey) throws UnsupportedEncodingException{
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + apiKey);
//sb.append("key=" + ConfigUtil.APP_SECRECT);
System.out.println("befor_md5_sign:"+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

/**
* @Description:将请求参数转换为xml格式的string
* @param parameters  请求参数
* @return
* @throws UnsupportedEncodingException
*/
public static String getRequestXml(SortedMap<Object,Object> parameters) throws UnsupportedEncodingException{
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();
}
/**
* @Description:返回给微信的参数
* @param return_code 返回编码
* @param return_msg  返回信息
* @return
*/
public static String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code
+ "]]></return_code><return_msg><![CDATA[" + return_msg
+ "]]></return_msg></xml>";
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: