java --微信支付
2016-05-26 18:03
507 查看
如下是微信支付工具类
package com.readygo.pet.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;import java.io.InputStreamReader;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import java.util.*;
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.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
/** * 程序名 :WXPayUtils.java * 程序功能 :微信支付工具类 * 作成者 :xx * 作成日期 :2016-04-27 * 修改履历 * 项目名 状态 作成者 作成日期 * ----------------------------------- * pets 新规 xx 2016-04-27 */
public class WXPayUtils {
/** 微信支付预支付单失败 */
public
static final
String MSG_WXPAY_ORDER_FAILED =
"微信支付连接失败,请检查网络。";
/** 微信支付失败 */
public
static final
String MSG_WXPAY_PAY_FAILED =
"微信支付付款失败。";
/** 微信支付查询 */
public
static final
String MSG_WXPAY_SEARCH_FAILED =
"微信支付付款失败。";
/** 微信支付关闭失败 */
public
static final
String MSG_WXPAY_CLOSE_FAILED =
"支付订单关闭失败。";
public
static final
String SunX509 =
"SunX509";
public
static final
String JKS =
"JKS";
public
static final
String PKCS12 =
"PKCS12";
public
static final
String TLS =
"TLS";
/** * 采番表对象枚举 */
public
enum TableType {
T_WXPAY_ORDER_RECORD("WX_PAY_ORDER_RECORD_ID"),
// 微信预支付订单记录表
T_WXPAY_RECORD("WX_PAY_RECORD_ID"),
// 微信支付记录表
T_WXPAY_CLOSE_RECORD("WX_PAY_CLOSE_RECORD_ID"),
// 微信支付订单关闭记录表
T_WXPAY_REFUND_RECORD("WX_PAY_REFUND_RECORD_ID");
// 微信退款订单记录表
private
String type;
private TableType(String type) {
this.type = type;
}
public
String getType() {
return
this.type;
}
}
/** * 错误码枚举 **/
public
enum ResultCode {
SUCCESS(0),
// 成功
FAILED(1),
// 失败
SUCCESS_UPLOADIMG(2);// 评论成功,上传图片失败
private
int type;
private ResultCode(Integer type) {
this.type = type;
}
public Integer getType() {
return
this.type;
}
}
/** * 获取系统时间 * * @return 系统时间(格式为:yyyyMMddHHmmss) */
public
static String getSysTime() {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
Date curDate = new Date();// 获取当前时间
String
str = df.format(curDate);
return
str;
}
/** * 获取ip地址 * * @param request * 请求包 * @return 本次请求的ip地址 */
public
static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/** * 对象转XML字符串 * 扩展xstream使其支持CDATA * * @param pi * 微信发起支付订单参数 * @return xml字符串 * */
/* * public static String payInfoToXML(WXPayParam pi) { xstream.alias("xml", * pi.getClass()); return xstream.toXML(pi); } * * private static XStream xstream = new XStream(new XppDriver() { public *
HierarchicalStreamWriter createWriter(Writer out) { return new * PrettyPrintWriter(out) { // 增加CDATA标记 boolean cdata = true; * * @SuppressWarnings("rawtypes") public void startNode(String name, Class * clazz) { super.startNode(name, clazz); } * * protected
void writeText(QuickWriter writer, String text) { if (cdata) { * writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } * else { writer.write(text); } } }; } }); */
/** * MAP转XML * * @param vo * 参数map * @param rootElement * 根节点 * @return xml字符串 */
public
static String map2xmlBody(Map<String,
Object> vo,
String rootElement) {
org.dom4j.Document doc = DocumentHelper.createDocument();
Element body = DocumentHelper.createElement(rootElement);
doc.add(body);
__buildMap2xmlBody(body, vo);
return doc.asXML();
}
@SuppressWarnings("unchecked")
private
static void __buildMap2xmlBody(Element body, Map<String,
Object> vo) {
if (vo !=
null) {
Iterator<String> it = vo.keySet().iterator();
while (it.hasNext()) {
String
key = (String) it.next();
if (!StringUtils.isEmpty(key)) {
Object obj = vo.get(key);
Element element = DocumentHelper.createElement(key);
if (obj !=
null) {
if (obj
instanceof java.lang.String) {
element.setText((String) obj);
} else {
if (obj
instanceof java.lang.Character || obj
instanceof java.lang.Boolean
|| obj instanceof java.lang.Number || obj
instanceof java.math.BigInteger
|| obj instanceof java.math.BigDecimal) {
org.dom4j.Attribute attr = DocumentHelper.createAttribute(element,
"type",
obj.getClass().getCanonicalName());
element.add(attr);
element.setText(String.valueOf(obj));
} else
if (obj
instanceof java.util.Map) {
org.dom4j.Attribute attr = DocumentHelper.createAttribute(element,
"type",
java.util.Map.class.getCanonicalName());
element.add(attr);
__buildMap2xmlBody(element, (Map<String,
Object>) obj);
} else {
}
}
}
body.add(element);
}
}
}
}
/** * XML转MAP * * @param xml * xml字符串 * @return map */
@SuppressWarnings("unchecked")
public
static SortedMap<String,
String> parseXml(String xml)
throws Exception {
SortedMap<String,
String>
map = new TreeMap<String,
String>();
if (!StringUtils.isEmpty(xml)) {
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
}
return
map;
}
/** * 生成签名 * * @param charaterEncodeing * 编码格式 * @param parameters * 请求参数(按ascii排序后) * @param apiKey * 商户支付秘钥 * @return */
@SuppressWarnings("rawtypes")
public
static String createSign(String charaterEncodeing, SortedMap<String,
Object> parameters,
String apiKey)
throws Exception {
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 (!StringUtils.isEmpty(v) && !"sing".equals(k) && !"key".equals(k))
{
sb.append(k +
"=" + v +
"&");
}
}
System.out.println("生成字符串:" + sb.toString());
sb.append("key=" + apiKey);
System.out.println("连接商户key字符串:" + sb.toString());
String sign = MD5Encode(sb.toString(), charaterEncodeing).toUpperCase();
return sign;
}
/** ----------------MD5相关方法--------------------- */
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 sb = new StringBuffer();
for (int i =
0; i < b.length; i++) {
sb.append(byteToHexString(b[i]));
}
return sb.toString();
}
/** * MD5加密 * * @param origin * 加密串 * @param charsetname * 编码格式(UTF-8) * @return 加密后字符串 * @throws Exception */
public
static String MD5Encode(String origin,
String charsetname)
throws Exception {
String resultString =
null;
resultString = new
String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (StringUtils.isEmpty(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
return resultString;
}
/** ----------------MD5相关方法--------------------- */
/** * 生成指定长度的随机字符串(英数字) * * @param length * 生成字符串的长度 * @return 随机字符串 */
public
static String getRandomString(int length) {
String base =
"abcdefghijklmnopqrstuvwxyz0123456789";
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();
}
/** * 带证书的HTTPS请求 * * @param requestURL * 请求地址 * @param outputStr * 请求参数 * @param certPath * 证书本地路径 * @param keyPasswd * 私密密码 * @return 请求结果 * @throws Exception * */
public
static String httpsRequestWithCert(String requestURL,
String outputStr,
String certPath,
String keyPasswd)
throws Exception {
StringBuffer buffer = new StringBuffer();
// P12私密
FileInputStream keyFileInputStream = new FileInputStream(new File(certPath));
final
char[] kp = keyPasswd.toCharArray();
KeyStore ks = KeyStore.getInstance(PKCS12);
ks.load(keyFileInputStream, kp);
keyFileInputStream.close();
// 相信自己的CA和所有自签名的证书
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(ks, kp).build();
// 只允许使用TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
new
String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// 创建http请求(post方式)
HttpPost httpPost = new HttpPost(requestURL);
System.out.println("executing request" + httpPost.getRequestLine());
StringEntity reqEntity = new StringEntity(outputStr);
// 设置类型
reqEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(reqEntity);
// 进行访问并获取返回
CloseableHttpResponse response = httpclient.execute(httpPost);
// 获取返回内容
HttpEntity entity = response.getEntity();
if (entity !=
null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader =
new
BufferedReader(new InputStreamReader(entity.getContent(),
"UTF-8"));
String
str =
null;
while ((str = bufferedReader.readLine()) !=
null) {
buffer.append(str);
}
bufferedReader.close();
}
// 关闭应该关闭的资源
EntityUtils.consume(entity);
response.close();
httpclient.close();
return buffer.toString();
}
}
package com.readygo.pet.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;import java.io.InputStreamReader;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import java.util.*;
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.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
/** * 程序名 :WXPayUtils.java * 程序功能 :微信支付工具类 * 作成者 :xx * 作成日期 :2016-04-27 * 修改履历 * 项目名 状态 作成者 作成日期 * ----------------------------------- * pets 新规 xx 2016-04-27 */
public class WXPayUtils {
/** 微信支付预支付单失败 */
public
static final
String MSG_WXPAY_ORDER_FAILED =
"微信支付连接失败,请检查网络。";
/** 微信支付失败 */
public
static final
String MSG_WXPAY_PAY_FAILED =
"微信支付付款失败。";
/** 微信支付查询 */
public
static final
String MSG_WXPAY_SEARCH_FAILED =
"微信支付付款失败。";
/** 微信支付关闭失败 */
public
static final
String MSG_WXPAY_CLOSE_FAILED =
"支付订单关闭失败。";
public
static final
String SunX509 =
"SunX509";
public
static final
String JKS =
"JKS";
public
static final
String PKCS12 =
"PKCS12";
public
static final
String TLS =
"TLS";
/** * 采番表对象枚举 */
public
enum TableType {
T_WXPAY_ORDER_RECORD("WX_PAY_ORDER_RECORD_ID"),
// 微信预支付订单记录表
T_WXPAY_RECORD("WX_PAY_RECORD_ID"),
// 微信支付记录表
T_WXPAY_CLOSE_RECORD("WX_PAY_CLOSE_RECORD_ID"),
// 微信支付订单关闭记录表
T_WXPAY_REFUND_RECORD("WX_PAY_REFUND_RECORD_ID");
// 微信退款订单记录表
private
String type;
private TableType(String type) {
this.type = type;
}
public
String getType() {
return
this.type;
}
}
/** * 错误码枚举 **/
public
enum ResultCode {
SUCCESS(0),
// 成功
FAILED(1),
// 失败
SUCCESS_UPLOADIMG(2);// 评论成功,上传图片失败
private
int type;
private ResultCode(Integer type) {
this.type = type;
}
public Integer getType() {
return
this.type;
}
}
/** * 获取系统时间 * * @return 系统时间(格式为:yyyyMMddHHmmss) */
public
static String getSysTime() {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
Date curDate = new Date();// 获取当前时间
String
str = df.format(curDate);
return
str;
}
/** * 获取ip地址 * * @param request * 请求包 * @return 本次请求的ip地址 */
public
static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
/** * 对象转XML字符串 * 扩展xstream使其支持CDATA * * @param pi * 微信发起支付订单参数 * @return xml字符串 * */
/* * public static String payInfoToXML(WXPayParam pi) { xstream.alias("xml", * pi.getClass()); return xstream.toXML(pi); } * * private static XStream xstream = new XStream(new XppDriver() { public *
HierarchicalStreamWriter createWriter(Writer out) { return new * PrettyPrintWriter(out) { // 增加CDATA标记 boolean cdata = true; * * @SuppressWarnings("rawtypes") public void startNode(String name, Class * clazz) { super.startNode(name, clazz); } * * protected
void writeText(QuickWriter writer, String text) { if (cdata) { * writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } * else { writer.write(text); } } }; } }); */
/** * MAP转XML * * @param vo * 参数map * @param rootElement * 根节点 * @return xml字符串 */
public
static String map2xmlBody(Map<String,
Object> vo,
String rootElement) {
org.dom4j.Document doc = DocumentHelper.createDocument();
Element body = DocumentHelper.createElement(rootElement);
doc.add(body);
__buildMap2xmlBody(body, vo);
return doc.asXML();
}
@SuppressWarnings("unchecked")
private
static void __buildMap2xmlBody(Element body, Map<String,
Object> vo) {
if (vo !=
null) {
Iterator<String> it = vo.keySet().iterator();
while (it.hasNext()) {
String
key = (String) it.next();
if (!StringUtils.isEmpty(key)) {
Object obj = vo.get(key);
Element element = DocumentHelper.createElement(key);
if (obj !=
null) {
if (obj
instanceof java.lang.String) {
element.setText((String) obj);
} else {
if (obj
instanceof java.lang.Character || obj
instanceof java.lang.Boolean
|| obj instanceof java.lang.Number || obj
instanceof java.math.BigInteger
|| obj instanceof java.math.BigDecimal) {
org.dom4j.Attribute attr = DocumentHelper.createAttribute(element,
"type",
obj.getClass().getCanonicalName());
element.add(attr);
element.setText(String.valueOf(obj));
} else
if (obj
instanceof java.util.Map) {
org.dom4j.Attribute attr = DocumentHelper.createAttribute(element,
"type",
java.util.Map.class.getCanonicalName());
element.add(attr);
__buildMap2xmlBody(element, (Map<String,
Object>) obj);
} else {
}
}
}
body.add(element);
}
}
}
}
/** * XML转MAP * * @param xml * xml字符串 * @return map */
@SuppressWarnings("unchecked")
public
static SortedMap<String,
String> parseXml(String xml)
throws Exception {
SortedMap<String,
String>
map = new TreeMap<String,
String>();
if (!StringUtils.isEmpty(xml)) {
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
}
return
map;
}
/** * 生成签名 * * @param charaterEncodeing * 编码格式 * @param parameters * 请求参数(按ascii排序后) * @param apiKey * 商户支付秘钥 * @return */
@SuppressWarnings("rawtypes")
public
static String createSign(String charaterEncodeing, SortedMap<String,
Object> parameters,
String apiKey)
throws Exception {
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 (!StringUtils.isEmpty(v) && !"sing".equals(k) && !"key".equals(k))
{
sb.append(k +
"=" + v +
"&");
}
}
System.out.println("生成字符串:" + sb.toString());
sb.append("key=" + apiKey);
System.out.println("连接商户key字符串:" + sb.toString());
String sign = MD5Encode(sb.toString(), charaterEncodeing).toUpperCase();
return sign;
}
/** ----------------MD5相关方法--------------------- */
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 sb = new StringBuffer();
for (int i =
0; i < b.length; i++) {
sb.append(byteToHexString(b[i]));
}
return sb.toString();
}
/** * MD5加密 * * @param origin * 加密串 * @param charsetname * 编码格式(UTF-8) * @return 加密后字符串 * @throws Exception */
public
static String MD5Encode(String origin,
String charsetname)
throws Exception {
String resultString =
null;
resultString = new
String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (StringUtils.isEmpty(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
return resultString;
}
/** ----------------MD5相关方法--------------------- */
/** * 生成指定长度的随机字符串(英数字) * * @param length * 生成字符串的长度 * @return 随机字符串 */
public
static String getRandomString(int length) {
String base =
"abcdefghijklmnopqrstuvwxyz0123456789";
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();
}
/** * 带证书的HTTPS请求 * * @param requestURL * 请求地址 * @param outputStr * 请求参数 * @param certPath * 证书本地路径 * @param keyPasswd * 私密密码 * @return 请求结果 * @throws Exception * */
public
static String httpsRequestWithCert(String requestURL,
String outputStr,
String certPath,
String keyPasswd)
throws Exception {
StringBuffer buffer = new StringBuffer();
// P12私密
FileInputStream keyFileInputStream = new FileInputStream(new File(certPath));
final
char[] kp = keyPasswd.toCharArray();
KeyStore ks = KeyStore.getInstance(PKCS12);
ks.load(keyFileInputStream, kp);
keyFileInputStream.close();
// 相信自己的CA和所有自签名的证书
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(ks, kp).build();
// 只允许使用TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
new
String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// 创建http请求(post方式)
HttpPost httpPost = new HttpPost(requestURL);
System.out.println("executing request" + httpPost.getRequestLine());
StringEntity reqEntity = new StringEntity(outputStr);
// 设置类型
reqEntity.setContentType("application/x-www-form-urlencoded");
httpPost.setEntity(reqEntity);
// 进行访问并获取返回
CloseableHttpResponse response = httpclient.execute(httpPost);
// 获取返回内容
HttpEntity entity = response.getEntity();
if (entity !=
null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader =
new
BufferedReader(new InputStreamReader(entity.getContent(),
"UTF-8"));
String
str =
null;
while ((str = bufferedReader.readLine()) !=
null) {
buffer.append(str);
}
bufferedReader.close();
}
// 关闭应该关闭的资源
EntityUtils.consume(entity);
response.close();
httpclient.close();
return buffer.toString();
}
}
相关文章推荐
- 有图有话 | 嘘……你屏蔽了哪个微信群?
- android微信支付问题总结
- 支付宝或者微信支付的回调处理
- JAVA版微信支付V3-完全版
- 微信开发模板
- 微信分享到朋友圈,分享给朋友JS代码
- 微信Android资源混淆打包工具
- 微信支付接口之心酸
- PHP微信支付开发之扫描支付(模式二)后如何回调
- 微信公众账号开发入门准备
- php的微信支付接口
- android 友盟分享 之微信分享一直卡在正在进入微信,问题
- 10分钟搞定支付宝和微信支付的各种填坑
- WeChat 微信公众号开发步骤
- iOS仿支付宝/微信支付键盘输入demo
- android接入原生第三方登录(微信登录、QQ登录、新浪微博登录)
- 微信渠道二维码来源统计分析
- iOS 添加微信分享sdk流程
- iOS 接入微信 支付宝 参数设置
- 微信红包指定总金额随机生成每个红包金额算法