一.使用场景
1.福袋机上的屏幕是一个Android平板,相当于一个Android手机
2.需要给用户生成一张二维码,让用户扫描付款
3.得到用户付款的消息后,转动对应的电机,给用户掉落福袋
二.准备资料
1.研究微信支付文档 https://pay.weixin.qq.com/wiki/doc/api/index.html 根据我的需求,我选择了Native支付
2.模式二比较简单,比较容易实现成功,所已选择了模式二
3.拿着公司提供的一个邮箱在微信公众平台https://mp.weixin.qq.com/cgi-bin/loginpage?t=wxm2-login&lang=zh_CN申请一个账号,选择申请的这个公众号为服务号,把公司的相关信息和一些申请资料提交后,1-3天就申请好了.
4.拿到我们申请的公众号后,我们还要花300元进行微信认证,得1-3天就认证好了(可以得到一个AppID)
5.在微信公平平台的微信支付页面,点击 申请接入,发现需要一个商户号,所以我们 现在去微信商户平台https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F注册一个账户(注意:在电脑自带的IE浏览器上进行操作,因为后边要弄财富通,在谷歌浏览器上识别不到财富通),提交一些公司资料后,过1-3天就申请好了(可以自己设置一个32位的秘钥,可以得到一个商户号)
6.有了AppID,秘钥和商户号就可以进行开发了,即使没有后台,全做到Android都可以实现了
7.看微信支付文档有统一下单接口,查询订单接口,关闭订单接口.
三.开发
优快云橙子紫了博客主 https://blog.youkuaiyun.com/u013164584/article/details/78030481的这片帖子 封装的比较好
1.导入依赖
//网络
implementation 'org.lucee:httpcomponents-httpclient:4.5.6'
//二维码
implementation 'com.google.zxing:core:3.2.1'
implementation 'cn.bingoogolapple:bga-qrcodecore:1.1.7@aar'
implementation 'cn.bingoogolapple:bga-zxing:1.1.7@aar'
2.PayCommonUtil,该工具类中的方法包含:生成签名,判断签名是否正确,生成二维码,xml和map转换,随机字符串,请求下单等等
package com.wjbzg.wxtestdome.util;
import android.content.Context;
import android.content.res.AssetManager;
import android.support.annotation.NonNull;
import android.util.Log;
import org.apache.http.conn.ssl.SSLContexts;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Created by ${szz} on 2019\6\5 0005
*/
public class PayCommonUtil {
//判断签名是否正确
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 = MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
return tenpaySign.equals(mysign);
}
/**
* 生成签名
* @param characterEncoding 字符编码
* @param parameters
* @return
*/
public static String createSign(String characterEncoding, SortedMap<String, Object> parameters) {
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=" + Constent.VALUE_API_KEY);
String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
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];
}
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();
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString
.getBytes("UTF-8")));
} catch (Exception exception) {
}
return resultString;
}
//随机字符串生成
public static String getRandomString(int length) { //length表示生成字符串的长度
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
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();
}
//请求xml组装
public static String getRequestXml(SortedMap<String, 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 key = (String) entry.getKey();
String value = (String) entry.getValue();
sb.append("<" + key + ">" + value + "</" + key + ">");
}
sb.append("</xml>");
return sb.toString();
}
//请求方法
public 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;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
// System.out.println("连接超时");
Log.e("Tag","连接超时");
ce.printStackTrace();
} catch (Exception e) {
// System.out.println("https请求异常");
Log.e("Tag","https请求异常"+e);
e.printStackTrace();
}
return null;
}
// public static String httpsRequest(Context context, String data) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException, CertificateException, NoSuchProviderException {
// String result = null;
// // 证书密码(默认为商户ID)
// String password = Constent.VALUE_MCH_ID;
// // 实例化密钥库
// KeyStore ks = KeyStore.getInstance("PKCS12");
// // 获得密钥库文件流
// AssetManager am = context.getResources().getAssets();
// InputStream fis = am.open("apiclient_cert.p12");
// // 加载密钥库
// ks.load(fis, password.toCharArray());
// // 关闭密钥库文件流
// fis.close();
// // 实例化密钥库
// KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// // 初始化密钥工厂
// kmf.init(ks, password.toCharArray());
// // 创建SSLContext
// SSLContext sslContext = SSLContexts.custom()
// .loadKeyMaterial(ks, Constent.VALUE_MCH_ID.toCharArray()) //加载证书密码,默认为商户ID
// .build();
// sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
// // 获取SSLSocketFactory对象
// SSLSocketFactory ssf = sslContext.getSocketFactory();
// URL url = new URL(Constent.URL_TUIKUAN);
// HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
// conn.setRequestMethod("POST");
// //设置当前实例使用的SSLSocketFactory
// conn.setSSLSocketFactory(ssf);
// conn.setDoOutput(true);
// conn.setDoInput(true);
// conn.connect();
// DataOutputStream out = new DataOutputStream(
// conn.getOutputStream());
// if (data != null)
// out.writeBytes(data);
// out.flush();
// out.close();
// //获取输入流
// BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// int code = conn.getResponseCode();
// if (HttpsURLConnection.HTTP_OK == code) {
// String temp = in.readLine();
// while (temp != null) {
// if (result != null)
// result += temp;
// else
// result = temp;
// temp = in.readLine();
// }
// }
// return result;
// }
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
}
@NonNull
public static SortedMap<Object, Object> getSortedMap(Map<String, String> map) {
//过滤空 设置 TreeMap
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = map.get(parameter);
String v = "";
if (null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
return packageParams;
}
//---------------------
// 作者:橙子紫了
// 来源:优快云
// 原文:https://blog.youkuaiyun.com/u013164584/article/details/78030481
// 版权声明:本文为博主原创文章,转载请附上博文链接!
}
3.MainActivty
package com.wjbzg.wxtestdome;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import com.wjbzg.wxtestdome.util.Constent;
import com.wjbzg.wxtestdome.util.PayCommonUtil;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONObject;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import cn.bingoogolapple.qrcode.core.BGAQRCodeUtil;
import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder;
public class MainActivity extends AppCompatActivity {
//Native支付
//统一下单:https://api.mch.weixin.qq.com/pay/unifiedorder
// 公众账号ID appid
// 商户号 mch_id
// 随机字符串 nonce_str 随机字符串,长度要求在32位以内。推荐随机数生成算法 秘钥
// 签名 sign
// 商品描述 body
// 商户订单号 out_trade_no
// 标价金额 total_fee
// 终端IP spbill_create_ip
// 通知地址 notify_url
// 交易类型 trade_type NATIVE -Native支付
ImageView img_ewm;
String time;
SortedMap<Object, Object> packageParams;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
updateUI(packageParams);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Handler handler2 = new Handler();
Runnable runnable2 = new Runnable() {
@Override
public void run() {
new Thread(new Runnable() {//每次都要开一个线程去查询订单情况,直到有用户支付成功的结果。
@Override
public void run() {
checkOrder();
}
}).start();
handler2.postDelayed(runnable2, 5000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
createQRCode();//生成二维码
}
private void initView() {
img_ewm = (ImageView) findViewById(R.id.img_ewm);
}
/**
* 生成二维码
*/
private void createQRCode() {
new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Void... params) {
// System.out.println("ds>>> 生成二维码成功");
return QRCodeEncoder.syncEncodeQRCode(unifiedOrder(), BGAQRCodeUtil.dp2px(MainActivity.this, 150));
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
Log.e("Tag","生成二维码成功");
img_ewm.setImageBitmap(bitmap);
handler2.post(runnable2);//在main中调用 查询订单
} else {
// System.out.println("ds>>> 生成二维码失败");
Log.e("Tag","生成二维码失败");
}
}
}.execute();
}
//1 统一下单
public String unifiedOrder() {
Log.e("Tag","****准备下单数据****");
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
parameterMap.put(Constent.APPID, Constent.VALUE_APPID);//公众号ID
parameterMap.put(Constent.MCH_ID, Constent.VALUE_MCH_ID);//商户号
parameterMap.put(Constent.NONCE_STR, PayCommonUtil.getRandomString(32));//随机字符串
parameterMap.put(Constent.BODY, "一瓶可乐");//商品描述
parameterMap.put(Constent.SIGN_TYPE, Constent.MD);//签名类型
// parameterMap.put(Constent.DETAIL, "");//商品详情
// parameterMap.put(Constent.ATTACH, "欧亚国际分店");//附加数据
time = System.currentTimeMillis() + "";
parameterMap.put(Constent.OUT_TRADE_NO, time);//商户订单号
// parameterMap.put(Constent.FEE_TYPE, Constent.CNY);//标价币种
parameterMap.put(Constent.TOTAL_FEE, "1");//标价金额
parameterMap.put(Constent.SPBILL_CREATE_IP, "127.0.0.1");//终端IP
parameterMap.put(Constent.TIME_START, System.currentTimeMillis() + "");//交易起始时间
//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的,不能携带参数。
parameterMap.put(Constent.NOTIFY_URL, "http://www.wjbzg.cn/");//通知地址(支付结果通知)
parameterMap.put(Constent.TRADE_TYPE, Constent.NATIVE);//交易类型
// parameterMap.put(Constent.PRODUCT_ID, "");//商品ID(和设备ID一起需知,扫码支付时必须传)
// parameterMap.put(Constent.LIMIT_PAY, Constent.NO_CREDIT);//指定支付方式
parameterMap.put(Constent.SIGN, PayCommonUtil.createSign(Constent.UTF, parameterMap));//签名
final String requestXML = PayCommonUtil.getRequestXml(parameterMap);//将请求组装成xml形式
Log.e("Tag","----下单的xml-----"+requestXML);
String result = PayCommonUtil.httpsRequest(
Constent.URL_TONGYI_XIADAN, Constent.POST,
requestXML);//调用统一支付接口返回String类型字符串
Log.e("Tag","----下单后微信后台返回的结果xml-----"+result);
Map<String, String> map = null;
try {
map = PayCommonUtil.xmlToMap(result);//将返回的结果转为map形式
} catch (Exception e) {
Log.e("Tag","---------下单失败----------"+e);
e.printStackTrace();
}
String string = map.toString();
Log.e("Tag","---------返回的结果xml转成Map.toString----------"+string);
String result_code = map.get("result_code");
Log.e("Tag","****下单结果****"+result_code);
if (result_code.equals("SUCCESS")){
//下单成功
String code_url = map.get("code_url");
Log.e("Tag","二维码的url为:"+ code_url);
return code_url;
}else {
return null;
}
}
// 2 查询订单 每5秒查询一次订单
public void checkOrder() {
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
parameterMap.put(Constent.APPID, Constent.VALUE_APPID);//公众号ID
parameterMap.put(Constent.MCH_ID, Constent.VALUE_MCH_ID);//商户号
parameterMap.put(Constent.OUT_TRADE_NO, time);//商户订单号
parameterMap.put(Constent.NONCE_STR, PayCommonUtil.getRandomString(32));//随机字符串
parameterMap.put(Constent.SIGN, PayCommonUtil.createSign(Constent.UTF, parameterMap));//签名
String requestXML = PayCommonUtil.getRequestXml(parameterMap);//将请求组装成xml形式
Log.e("Tag","调用查询订单准备的xml:"+requestXML);
String result = PayCommonUtil.httpsRequest(
Constent.URL_CHAXUN_DINGDAN, Constent.POST,
requestXML);//调用查询订单接口返回String类型字符串
Log.e("Tag","调用查询订单接口返回String类型字符串:"+result);
Map<String, String> map = null;
try {
if (result != null)
map = PayCommonUtil.xmlToMap(result);//将返回的结果转为map形式
} catch (Exception e) {
e.printStackTrace();
}
if (map != null) {
packageParams = PayCommonUtil.getSortedMap(map);
}
if (packageParams != null && PayCommonUtil.isTenpaySign(Constent.UTF, packageParams, Constent.VALUE_API_KEY)) {
handler.post(runnable);//在新的线程去匹配返回的信息,做相应的UI变化
} else {
System.out.println("通知签名验证失败");
}
}
private void updateUI(SortedMap<Object, Object> packageParams) throws InterruptedException {
String result_code = (String) packageParams.get(Constent.RESULT_CODE);
String return_code = (String) packageParams.get(Constent.RETURN_CODE);
String trade_state = (String) packageParams.get(Constent.TRADE_STATE);
String trade_state_desc = (String) packageParams.get(Constent.TRADE_STATE_DESC);
String error_code = (String) packageParams.get(Constent.ERROR_CODE);
Log.e("Tag","result_code=" + result_code + ", return_code=" + return_code + ", trade_state=" + trade_state);
Log.e("Tag","trade_state_desc=" + trade_state_desc + ", error_code=" + error_code);
if (result_code.equals(Constent.SUCCESS) && return_code.equals(Constent.SUCCESS) && trade_state.equals(Constent.SUCCESS)) {
handler2.removeCallbacks(runnable2);
//支付成功,做相关逻辑。
} else if (Constent.PAYERROR.equals(packageParams.get(Constent.TRADE_STATE))) {
handler2.removeCallbacks(runnable2);
//支付失败,做相关逻辑。
}
}
}
3.Constent
package com.wjbzg.wxtestdome.util;
/**
* Created by ${szz} on 2019\6\5 0005
*/
public class Constent {
//秘钥
public static final String VALUE_API_KEY = "";
//商户ID kv
public static final String MCH_ID = "mch_id";
public static final String VALUE_MCH_ID = "";
//退款url
// public static final String URL_TUIKUAN = ;
//APPID kv
public static final String APPID = "appid";
public static final String VALUE_APPID = "";
//随机字符串 k
public static final String NONCE_STR = "nonce_str";
//商品描述 k
public static final String BODY = "body";
//签名类型 kv
public static final String SIGN_TYPE = "sign_type";
public static final String MD = "MD5";
//商品详情
// public static final String DETAIL = ;
//附加数据
public static final String ATTACH = "ATTACH";
//商户订单号 k
public static final String OUT_TRADE_NO = "out_trade_no";
//标价金额 k
public static final String TOTAL_FEE = "total_fee";
//终端IP k
public static final String SPBILL_CREATE_IP = "spbill_create_ip";
//交易起始时间 k
public static final String TIME_START = "time_start";
//异步接收微信支付结果通知的回调地址 K
public static final String NOTIFY_URL = "notify_url";
//交易类型
public static final String TRADE_TYPE = "trade_type";
public static final String NATIVE = "NATIVE";
public static final String SIGN = "sign";
public static final String UTF = "UTF-8";
public static final String URL_TONGYI_XIADAN = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public static final String POST = "POST";
public static final String CODE_URL = "CODE_URL";
//2.查询订单接口
public static final String URL_CHAXUN_DINGDAN = "https://api.mch.weixin.qq.com/pay/orderquery";
public static final String RESULT_CODE = "return_code";
public static final String RETURN_CODE = "result_code";
public static final String TRADE_STATE = "trade_state";
public static final String TRADE_STATE_DESC = "trade_state_desc";
public static final String ERROR_CODE = "err_code";
public static final String SUCCESS = "SUCCESS";
public static final String PAYERROR = "PAYERROR";
}
9238

被折叠的 条评论
为什么被折叠?



