
关于NET-C#开发HBuillder的APP支付(支付宝支付+微信支付)经验分享
NET-C#开发HBuillder的APP ,QQ群 :661996236
最近由于连续太多人找我咨询NET_C#开发支付功能,请求协助和demo,所以本人在此简单分享一下,其实在本社区内,已有一部分代码了。
链接:C#配置支付宝信息
APP前端开发调用代码示例,由于是项目部分代码,请自行完善APP前端支付代码,返回测试数据,只是演示,数据自行测试输出,由于
备注 : 获取支付通道
APP端调用代码说明
//定义支付通道
var channel = null;
var channelali = null;
var channelwx = null;
//获取支付通道的方法
function getChannels() {
plus.payment.getChannels(function (channels) {
channelali = channels[0];
channelwx = channels[1];
// for (var i = 0; i < channels.length; i++) {
//
// //if(mr=="支付宝")
// //{
// if (channels[i].id == "alipay")
// { //wxpay //alipay
// channelali = channels[i];
// }
// //}
// //if(mr=="微信")
// //{
// if (channels[i].id == "wxpay")
// { //wxpay //alipay
// channelwx = channels[i];
// }
// // }
// }
// plus.ui.toast("使用支付方式:" + channel.id);
}, function (e) {
plus.ui.toast("获取支付通道失败!");
console.log("获取支付通道失败!");
})
}
//支付事件
mui('.gouwuche').on('tap', '#waitpay', function (event) {
channel = null;
var payv = g("pay").value;
console.log(payv);
if (payv == '2') {
urlpay = "OrderPay/GetWxPayInfo";
console.log(urlpay);
} else {
urlpay = "OrderPay/PayMent";
console.log(urlpay);
}
//获取支付通道
// getChannels( );
// console.log(channel.id);
//apiurl + "AliPay/testsign" OrderPay/PayMent Weixin/GetPayInfo
postwait(apiurl + urlpay, returnJson({
orderid: order_no,
type: '3'
}), function (data) {
//console.log(data);
var err = JSON.parse(data).err;
if (err == 0) {
var appdata = JSON.parse(data).data;
//console.log(m);
getChannels();
var pay = g("pay").value;
console.log(appdata.msg);
if (pay == 1) //channel.id== 'alipay')
{
appdata = appdata.msg; //JSON.parse(appdata).msg; //支付宝
channel = channelali;
console.log(channelali.id);
} else {
channel = channelwx;
console.log(channelwx.id);
// console.log("==微信信息=="+ JSON.stringify(appdata));
// appdata = JSON.stringify(appdata); //微信
}
// console.log("==支付信息=="+ appdata);
//appdata='{"appid":"wx123456789,"noncestr":"QtyOdJUspxLGOG5c","package":"Sign=WXPay","partnerid":"12345678","prepayid":"wx20179876543234","timestamp":1496823681,"sign":"DD2D77CE3A770C497A03B3BCEABEF12C"}';
plus.payment.request(channel, appdata, function (result) {
// 弹出系统提示对话框
//mui.toast("支付成功!");
plus.nativeUI.alert( "支付成功!",
function()
{
//修改订单状态
postwait(apiurl + "OrderPay/UpdateOrderStatus", returnJson({
orderid: order_no,
type: 3
}), function (data) {
console.log("修改:"+data);
//openwindows("../../index.html","index.html");
//alert('22');
//alert("end");
var obj = plus.webview.getWebviewById("shopsDetails");
if(obj!=null){ obj.reload(); }
plus.webview.getLaunchWebview().evalJS("orderlist();");
plus.webview.getLaunchWebview().show();
}
);
}, "测试支付", "确认" );
}, function (error) {
if (error.code == 62001) {
mui.toast("您放弃支付.");
// mui.back();
} else {
//alert(error.message);
mui.toast("请前往订单管理尝试支付.");
// plus.nativeUI.alert("支付失败", null, "请前往订单管理尝试支付,支付失败:"+error.code );
}
var obj = plus.webview.getWebviewById("shopsDetails");
if(obj!=null){ obj.reload(); }
plus.webview.getLaunchWebview().evalJS("orderlist();");
plus.webview.getLaunchWebview().show();
});
//
} else {
var msg = JSON.parse(data).msg; //JSON.parse(JSON.parse(data).data).error; //JSON.parse(data).msg;
mui.toast(msg);
}
}
);
//跳转
var obj = plus.webview.getWebviewById("middle_main.html");
if(obj!=null)
obj.reload();
//plus.webview.currentWebview().close();
openwindows("/pages/home/middle_main.html", "middle_main.html", { address_id: 1 });
});
NET-C#支付宝代码验证以及生成支付字符串
支付宝支付
支付宝参考:
链接:C#配置支付宝信息
本文使用C# WebAPI方式
/// <summary>
/// 获取支付信息
/// </summary>
/// <param name="_amount"></param>
/// <returns></returns>
[HttpPost, Route("Alipay/GetPayInfo")]
public string GetPayInfo(string orderid)//_amount:付款金额
{
string strJson = string.Empty;
try
{
//string _amount = "0.01";
string orderInfo = GetOrderInfoWithOutEncode(orderid);
// 对订单做RSA 签名
string sign = RSAFromPkcs8.sign(orderInfo, Config.privtekey, "utf-8");
//仅需对sign做URL编码
sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);
string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;
strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
DTcms.Common.FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
}
catch (Exception ex)
{
DTcms.Common.FileOperate.WriteLogInfo("", "GetPayInfo Exception:" + ex.Message);
//FileLog.WriteLog(ex.ToString());
}
return strJson;
}
/// <summary>
/// 不包含Encode的字符串拼接
/// </summary>
/// <param name="price"></param>
/// <returns></returns>
public string GetOrderInfoWithOutEncode(string orderid)
{
// DTcms.Common.FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
PayInfo.Add("app_id", Config.app_id);
PayInfo.Add("biz_content", GetBizContent(orderid));
PayInfo.Add("charset", "utf-8");
PayInfo.Add("format", "json");
PayInfo.Add("method", "alipay.trade.app.pay");
PayInfo.Add("notify_url", "http://www.baidu.com/OrderPay/PayUpdateStatus");
PayInfo.Add("sign_type", "RSA");
PayInfo.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
PayInfo.Add("version", "1.0");
string strUrl = BuildQueryWithOutEncode(PayInfo);
return strUrl;
}
/// <summary>
/// 获取支付内容详情
/// </summary>
/// <param name="total_amount"></param>
/// <returns></returns>
public string GetBizContent(string orderid)
{
string strSql = string.Format(@"select dt_shop_orders.id,dt_shop_orders.order_no,dt_shop_orders.order_amount,dt_shop_order_goods.goods_title from dt_shop_orders
left join dt_shop_order_goods on dt_shop_orders.id = dt_shop_order_goods.order_id where dt_shop_orders.id = {0}", orderid);
DataTable dt = DbHelperSQL.Query(strSql).Tables[0];
Dictionary<string, string> biz_content_info = new Dictionary<string, string>();
biz_content_info.Add("timeout_express", "30m");//该笔订单允许的最晚付款时间,逾期将关闭交易。
biz_content_info.Add("seller_id", "");//收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
biz_content_info.Add("product_code", "QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
biz_content_info.Add("total_amount", dt.Rows[0]["order_amount"].ToString());//订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
biz_content_info.Add("subject", dt.Rows[0]["goods_title"].ToString() + "……");//商品的标题/交易标题/订单标题/订单关键字等。
biz_content_info.Add("body", dt.Rows[0]["order_no"].ToString());//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
biz_content_info.Add("out_trade_no", orderid);//商户网站唯一订单号
string strBizContent = JsonConvert.SerializeObject(biz_content_info, timejson);
return strBizContent;
}
/// <summary>
/// 组装普通文本请求参数(不带Encode)。
/// </summary>
/// <param name="parameters">Key-Value形式请求参数字典</param>
/// <returns>URL编码后的请求数据</returns>
public static string BuildQueryWithOutEncode(IDictionary<string, string> parameters)
{
StringBuilder postData = new StringBuilder();
bool hasParam = false;
IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();
while (dem.MoveNext())
{
string name = dem.Current.Key;
string value = dem.Current.Value;
// 忽略参数名或参数值为空的参数
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
{
if (hasParam)
{
postData.Append("&");
}
postData.Append(name);
postData.Append("=");
string encodedValue = value;
postData.Append(encodedValue);
hasParam = true;
}
}
return postData.ToString();
}
/// <summary>
/// 配置(请自行填上下面两个参数) 注释的部分代码不需要,只需要配置没有注释的4个基础参数
/// </summary>
public class Config
{
/// <summary>
/// 应用APPID
/// </summary>
public const string app_id = "自行填写";
/// <summary>
/// 私钥,通过工具生成 //这个就是生成器里面的那个私钥,第一个大框框那里的.
/// </summary>
/// rsa_private_key.pem 用于官方SDK获取支付宝支付信息
public const string privtekeyNocs8 = "自行填写";
//rsa_private_key_pkcs8.pem 用于获取支付宝支付信息
public const string privtekey = "自行填写";
//string APPID = "2017052207309344";
///// 私钥,通过工具生成 //这个就是生成器里面的那个私钥,第一个大框框那里的.
//rsa_private_key.pem 密钥
//string APP_PRIVATE_KEY = "自行填写";
//rsa_private_key_pkcs8.pem pkcs8密钥
//string APP_PRIVATE_KEY = "自行填写";
//配置支付宝的公钥 用于异步通知
public const string ALIPAY_PUBLIC_KEY = "自行填写";
//string CHARSET = "utf-8";
}
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Common.RSAFromPkcs
{
/// <summary>
/// 类名:RSAFromPkcs8
/// 功能:RSA解密、签名、验签
/// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改
/// 版本:2.0
/// 修改日期:2011-05-10
/// 说明:
/// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
/// 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
/// </summary>
public sealed class RSAFromPkcs8
{
/// <summary>
/// 签名
/// </summary>
/// <param name="content">需要签名的内容</param>
/// <param name="privateKey">私钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns></returns>
public static string sign(string content, string privateKey, string input_charset)
{
Encoding code = Encoding.GetEncoding(input_charset);
byte[] Data = code.GetBytes(content);
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
byte[] signData = rsa.SignData(Data, sh);
return Convert.ToBase64String(signData);
}
/// <summary>
/// 验证签名
/// </summary>
/// <param name="content">需要验证的内容</param>
/// <param name="signedString">签名结果</param>
/// <param name="publicKey">公钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns></returns>
public static bool verify(string content, string signedString, string publicKey, string input_charset)
{
bool result = false;
Encoding code = Encoding.GetEncoding(input_charset);
byte[] Data = code.GetBytes(content);
byte[] data = Convert.FromBase64String(signedString);
RSAParameters paraPub = ConvertFromPublicKey(publicKey);
RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();
rsaPub.ImportParameters(paraPub);
SHA1 sh = new SHA1CryptoServiceProvider();
result = rsaPub.VerifyData(Data, sh, data);
return result;
}
/// <summary>
/// 用RSA解密
/// </summary>
/// <param name="resData">待解密字符串</param>
/// <param name="privateKey">私钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns>解密结果</returns>
public static string decryptData(string resData, string privateKey, string input_charset)
{
byte[] DataToDecrypt = Convert.FromBase64String(resData);
List<byte> result = new List<byte>();
for (int j = 0; j < DataToDecrypt.Length / 128; j++)
{
byte[] buf = new byte[128];
for (int i = 0; i < 128; i++)
{
buf[i] = DataToDecrypt[i + 128 * j];
}
result.AddRange(decrypt(buf, privateKey, input_charset));
}
byte[] source = result.ToArray();
char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];
Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);
return new string(asciiChars);
}
private static byte[] decrypt(byte[] data, string privateKey, string input_charset)
{
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
return rsa.Decrypt(data, false);
}
/// <summary>
/// 解析java生成的pem文件私钥
/// </summary>
/// <param name="pemstr"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)
{
byte[] pkcs8privatekey;
pkcs8privatekey = Convert.FromBase64String(pemstr);
if (pkcs8privatekey != null)
{
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
return rsa;
}
else
return null;
}
private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
{
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x02)
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0001)
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if (bt != 0x04) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
#region 解析.net 生成的Pem
private static RSAParameters ConvertFromPublicKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 162)
{
throw new ArgumentException("pem file content is incorrect.");
}
byte[] pemModulus = new byte[128];
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, 29, pemModulus, 0, 128);
Array.Copy(keyData, 159, pemPublicExponent, 0, 3);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
return para;
}
private static RSAParameters ConvertFromPrivateKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 609)
{
throw new ArgumentException("pem file content is incorrect.");
}
int index = 11;
byte[] pemModulus = new byte[128];
Array.Copy(keyData, index, pemModulus, 0, 128);
index += 128;
index += 2;//141
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, index, pemPublicExponent, 0, 3);
index += 3;
index += 4;//148
byte[] pemPrivateExponent = new byte[128];
Array.Copy(keyData, index, pemPrivateExponent, 0, 128);
index += 128;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279
byte[] pemPrime1 = new byte[64];
Array.Copy(keyData, index, pemPrime1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346
byte[] pemPrime2 = new byte[64];
Array.Copy(keyData, index, pemPrime2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413
byte[] pemExponent1 = new byte[64];
Array.Copy(keyData, index, pemExponent1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480
byte[] pemExponent2 = new byte[64];
Array.Copy(keyData, index, pemExponent2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546
byte[] pemCoefficient = new byte[64];
Array.Copy(keyData, index, pemCoefficient, 0, 64);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
para.D = pemPrivateExponent;
para.P = pemPrime1;
para.Q = pemPrime2;
para.DP = pemExponent1;
para.DQ = pemExponent2;
para.InverseQ = pemCoefficient;
return para;
}
#endregion
}
}
微信支付
#region 微信支付
[HttpPost, Route("OrderPay/GetWxPayInfo")]
public string GetWxPayInfo(dynamic obj)
{
string strJson = string.Empty;
try
{
string orderid = Convert.ToString(obj.orderid);
Model.dt_shop_orders orderEntity = new BLL.dt_shop_orders().GetModel(int.Parse(orderid));
if (orderEntity != null && orderEntity.id > 0)
{
decimal amount = orderEntity.order_amount;
string horderId = orderEntity.id.ToString();
string trade_no = orderEntity.order_no.ToString();
string hopenid = "";
string parameters = WxPayDataGet(amount, horderId, trade_no, hopenid, horderId);
return new Common.DotNetJson.JSONAPI().ToJson(parameters, "获取微信支付信息成功!", 0);
//JsonConvert.SerializeObject("{\"msg\":\"" + parameters + "\",\"type\":\"wxpay\"}"), "获取微信支付信息成功!", 0);
}
else
{
return new Common.DotNetJson.JSONAPI().ToJson(JsonConvert.SerializeObject("{\"error\":\"当前订单已失效!\"}"), "获取支付信息失败!", 1);
}
}
catch (Exception ex)
{
FileOperate.WriteLogInfo("", "GetPayInfo Exception:" + ex.Message);
return new Common.DotNetJson.JSONAPI().ToJson("{\"error\":\"" + ex.Message + "\"}", "获取微信支付信息失败!", 1);
}
}
/// <summary>
/// 微信支付:生成请求数据
/// </summary>
/// <param name="openid">微信用户id</openid>
/// <param name="ttFee">商品总价格</param>
/// <param name="busiBody"></param>
/// <returns></returns>
protected string WxPayDataGet(decimal ttFee, string busiBody, string out_trade_no, string openid, string otid)
{
//BLL.wx_payment_wxpay wxPayBll = new BLL.wx_payment_wxpay();
//Model.wx_payment_wxpay paymentInfo = wxPayBll.GetModelByWid(wid);
//先设置基本信息
//string partnerId = ""; // paymentInfo.partnerId;// " ";//
//string appId = ""; // paymentInfo.appId;// " ";//
//string partnerKey = ""; // paymentInfo.partnerKey;// " ";//
////paysignkey(非appkey)
//string appKey = ""; // paymentInfo.paySignKey;
//--
JsApiPay jsApiPay = new JsApiPay();
jsApiPay.openid = openid;
jsApiPay.total_fee = int.Parse(((int)(ttFee * 100)).ToString());
string attch = 3 + "|" + otid + "|我爱我县";
WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult(openid, otid, attch, "woaiwoxian");
WxPayData jsApiParam = new WxPayData();
string time = WxPayApi.GenerateTimeStamp();
string nonceStr = WxPayApi.GenerateNonceStr();
string pack = "prepay_id=" + unifiedOrderResult.GetValue("prepay_id");
//**************************************************封装调起微信客户端支付界面字符串********************
//设置待加密支付参数并加密
string appid = unifiedOrderResult.GetValue("appid").ToString();
string mch_Id = unifiedOrderResult.GetValue("mch_id").ToString();
string prepay_id = unifiedOrderResult.GetValue("prepay_id").ToString();
jsApiParam.SetValue("appId", appid); //
jsApiParam.SetValue("partnerid", mch_Id);
jsApiParam.SetValue("prepayid", prepay_id);
jsApiParam.SetValue("package", "Sign=WXPay");
jsApiParam.SetValue("nonceStr", nonceStr);
jsApiParam.SetValue("timeStamp", time);
string sign = jsApiParam.MakeSign();
// jsApiParam.SetValue("sign", sign);
//设置支付包参数
WxPayData paydata = new WxPayData();
paydata.SetValue("retcode", 0);//5+固定调起参数
paydata.SetValue("retmsg", "ok");//5+固定调起参数
paydata.SetValue("appid", appid);//AppId,微信开放平台新建应用时产生
paydata.SetValue("partnerid", mch_Id);//商户编号,微信开放平台申请微信支付时产生
paydata.SetValue("prepayid", prepay_id);//由上面获取预支付流程获取
paydata.SetValue("package", "Sign=WXpay");//APP支付固定设置参数
paydata.SetValue("noncestr", nonceStr);//随机字符串,
paydata.SetValue("timestamp", time);//时间戳
Hashtable paySignReqHandler = new Hashtable();
paySignReqHandler.Add("appid", appid);
paySignReqHandler.Add("partnerid", mch_Id);
paySignReqHandler.Add("prepayid", prepay_id);
paySignReqHandler.Add("noncestr", nonceStr);
paySignReqHandler.Add("package", "Sign=WXpay");
paySignReqHandler.Add("timestamp", time.ToString());
var paySign = CreateMd5Sign(paySignReqHandler);
paydata.SetValue("sign", paySign);//时间戳
//var serializer = new JavaScriptSerializer();
//var result = serializer.Serialize(obj);
//paySign = HttpUtility.UrlEncode(paySign, Encoding.UTF8);
//string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;
//strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
// FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
string result = paydata.ToJson();
// FileOperate.WriteLogInfo("", "result:" + result);
return result;
}
private string CreateMd5Sign(Hashtable parameters)
{
var sb = new StringBuilder();
var akeys = new ArrayList(parameters.Keys);
akeys.Sort();//排序,这是微信要求的
foreach (string k in akeys)
{
var v = (string)parameters[k];
sb.Append(k + "=" + v + "&");
}
sb.Append("key=" + WxPayAPI.WxPayConfig.KEY);
string sign = GetMD5(sb.ToString());
return sign;
}
private string GetMD5(string src)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] data = Encoding.UTF8.GetBytes(src);
byte[] md5data = md5.ComputeHash(data);
md5.Clear();
var retStr = BitConverter.ToString(md5data);
retStr = retStr.Replace("-", "").ToUpper();
return retStr;
}
#endregion
//=========================================================================
//微信支付demo里面的部分代码 JsApiPay ,其他的代码基本在微信sdk demo里面基本完成,细节部分自己待处理就好了
/**
* 调用统一下单,获得下单结果
* @return 统一下单结果
* @失败时抛异常WxPayException
*/
public WxPayData GetUnifiedOrderResult(string hasopenid, string body, string attach, string tag)
{
string strSql = string.Format(@"select dt_shop_orders.id,dt_shop_orders.order_no,dt_shop_orders.order_amount,dt_shop_order_goods.goods_title from dt_shop_orders
left join dt_shop_order_goods on dt_shop_orders.id = dt_shop_order_goods.order_id where dt_shop_orders.id = {0}", body);
DataTable dt = DbHelperSQL.Query(strSql).Tables[0];
//统一下单
WxPayData data = new WxPayData();
data.SetValue("body", dt.Rows[0]["goods_title"].ToString() + "...");
data.SetValue("attach", attach);
data.SetValue("out_trade_no", body);
data.SetValue("total_fee", total_fee);
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
data.SetValue("goods_tag", tag);
data.SetValue("trade_type", "APP");
data.SetValue("openid", hasopenid);
//异步通知url未设置,则使用配置文件中的url
data.SetValue("notify_url", "http://异步通知url/OrderPay/WXUpdateStatus");//异步通知url OrderPay/WXUpdateStatus
data.SetValue("appid", WxPayConfig.APPID);//公众账号ID
data.SetValue("mch_id", WxPayConfig.MCHID);//商户号
data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
//签名
data.SetValue("sign", data.MakeSign());
WxPayData result = WxPayApi.UnifiedOrder(data);
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
{
Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
throw new WxPayException("UnifiedOrder response error!");
}
unifiedOrderResult = result;
return result;
}
支付宝
Android&iOS
错误码 错误描述
8000 正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
4000 订单支付失败
5000 重复请求
6001 用户中途取消
6002 网络连接出错
6004 支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
微信支付
Android&iOS
错误码 错误描述
-1 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等
-2 无需处理。发生场景:用户不支付了,点击取消,返回APP。
-3 发送失败
-4 授权失败
-5 微信不支持
-
最后说明: 支付宝支付签名是 rsa_private_key_pkcs8 这个方式,千万不搞错了,本文使用是RSA,不是RSA2,然后支付宝 公钥 私钥不要搞错了 ,支付宝支付支持在线调试,微信不支持,需要打包安装才能测试支付功能,
微信支付还有一个点,密钥需要一致,不能错误!
由于时间匆忙,贴了一些代码,需要各位同学自行完善和修改,如有错误,欢迎指出我来及时修改完善,其他问题, 欢迎加群来咨询交流学习!谢谢大家
NET-C#开发HBuillder的APP ,QQ群 :661996236
最近由于连续太多人找我咨询NET_C#开发支付功能,请求协助和demo,所以本人在此简单分享一下,其实在本社区内,已有一部分代码了。
链接:C#配置支付宝信息
APP前端开发调用代码示例,由于是项目部分代码,请自行完善APP前端支付代码,返回测试数据,只是演示,数据自行测试输出,由于
备注 : 获取支付通道
APP端调用代码说明
//定义支付通道
var channel = null;
var channelali = null;
var channelwx = null;
//获取支付通道的方法
function getChannels() {
plus.payment.getChannels(function (channels) {
channelali = channels[0];
channelwx = channels[1];
// for (var i = 0; i < channels.length; i++) {
//
// //if(mr=="支付宝")
// //{
// if (channels[i].id == "alipay")
// { //wxpay //alipay
// channelali = channels[i];
// }
// //}
// //if(mr=="微信")
// //{
// if (channels[i].id == "wxpay")
// { //wxpay //alipay
// channelwx = channels[i];
// }
// // }
// }
// plus.ui.toast("使用支付方式:" + channel.id);
}, function (e) {
plus.ui.toast("获取支付通道失败!");
console.log("获取支付通道失败!");
})
}
//支付事件
mui('.gouwuche').on('tap', '#waitpay', function (event) {
channel = null;
var payv = g("pay").value;
console.log(payv);
if (payv == '2') {
urlpay = "OrderPay/GetWxPayInfo";
console.log(urlpay);
} else {
urlpay = "OrderPay/PayMent";
console.log(urlpay);
}
//获取支付通道
// getChannels( );
// console.log(channel.id);
//apiurl + "AliPay/testsign" OrderPay/PayMent Weixin/GetPayInfo
postwait(apiurl + urlpay, returnJson({
orderid: order_no,
type: '3'
}), function (data) {
//console.log(data);
var err = JSON.parse(data).err;
if (err == 0) {
var appdata = JSON.parse(data).data;
//console.log(m);
getChannels();
var pay = g("pay").value;
console.log(appdata.msg);
if (pay == 1) //channel.id== 'alipay')
{
appdata = appdata.msg; //JSON.parse(appdata).msg; //支付宝
channel = channelali;
console.log(channelali.id);
} else {
channel = channelwx;
console.log(channelwx.id);
// console.log("==微信信息=="+ JSON.stringify(appdata));
// appdata = JSON.stringify(appdata); //微信
}
// console.log("==支付信息=="+ appdata);
//appdata='{"appid":"wx123456789,"noncestr":"QtyOdJUspxLGOG5c","package":"Sign=WXPay","partnerid":"12345678","prepayid":"wx20179876543234","timestamp":1496823681,"sign":"DD2D77CE3A770C497A03B3BCEABEF12C"}';
plus.payment.request(channel, appdata, function (result) {
// 弹出系统提示对话框
//mui.toast("支付成功!");
plus.nativeUI.alert( "支付成功!",
function()
{
//修改订单状态
postwait(apiurl + "OrderPay/UpdateOrderStatus", returnJson({
orderid: order_no,
type: 3
}), function (data) {
console.log("修改:"+data);
//openwindows("../../index.html","index.html");
//alert('22');
//alert("end");
var obj = plus.webview.getWebviewById("shopsDetails");
if(obj!=null){ obj.reload(); }
plus.webview.getLaunchWebview().evalJS("orderlist();");
plus.webview.getLaunchWebview().show();
}
);
}, "测试支付", "确认" );
}, function (error) {
if (error.code == 62001) {
mui.toast("您放弃支付.");
// mui.back();
} else {
//alert(error.message);
mui.toast("请前往订单管理尝试支付.");
// plus.nativeUI.alert("支付失败", null, "请前往订单管理尝试支付,支付失败:"+error.code );
}
var obj = plus.webview.getWebviewById("shopsDetails");
if(obj!=null){ obj.reload(); }
plus.webview.getLaunchWebview().evalJS("orderlist();");
plus.webview.getLaunchWebview().show();
});
//
} else {
var msg = JSON.parse(data).msg; //JSON.parse(JSON.parse(data).data).error; //JSON.parse(data).msg;
mui.toast(msg);
}
}
);
//跳转
var obj = plus.webview.getWebviewById("middle_main.html");
if(obj!=null)
obj.reload();
//plus.webview.currentWebview().close();
openwindows("/pages/home/middle_main.html", "middle_main.html", { address_id: 1 });
});
NET-C#支付宝代码验证以及生成支付字符串
支付宝支付
支付宝参考:
链接:C#配置支付宝信息
本文使用C# WebAPI方式
/// <summary>
/// 获取支付信息
/// </summary>
/// <param name="_amount"></param>
/// <returns></returns>
[HttpPost, Route("Alipay/GetPayInfo")]
public string GetPayInfo(string orderid)//_amount:付款金额
{
string strJson = string.Empty;
try
{
//string _amount = "0.01";
string orderInfo = GetOrderInfoWithOutEncode(orderid);
// 对订单做RSA 签名
string sign = RSAFromPkcs8.sign(orderInfo, Config.privtekey, "utf-8");
//仅需对sign做URL编码
sign = HttpUtility.UrlEncode(sign, Encoding.UTF8);
string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;
strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
DTcms.Common.FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
}
catch (Exception ex)
{
DTcms.Common.FileOperate.WriteLogInfo("", "GetPayInfo Exception:" + ex.Message);
//FileLog.WriteLog(ex.ToString());
}
return strJson;
}
/// <summary>
/// 不包含Encode的字符串拼接
/// </summary>
/// <param name="price"></param>
/// <returns></returns>
public string GetOrderInfoWithOutEncode(string orderid)
{
// DTcms.Common.FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
PayInfo.Add("app_id", Config.app_id);
PayInfo.Add("biz_content", GetBizContent(orderid));
PayInfo.Add("charset", "utf-8");
PayInfo.Add("format", "json");
PayInfo.Add("method", "alipay.trade.app.pay");
PayInfo.Add("notify_url", "http://www.baidu.com/OrderPay/PayUpdateStatus");
PayInfo.Add("sign_type", "RSA");
PayInfo.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
PayInfo.Add("version", "1.0");
string strUrl = BuildQueryWithOutEncode(PayInfo);
return strUrl;
}
/// <summary>
/// 获取支付内容详情
/// </summary>
/// <param name="total_amount"></param>
/// <returns></returns>
public string GetBizContent(string orderid)
{
string strSql = string.Format(@"select dt_shop_orders.id,dt_shop_orders.order_no,dt_shop_orders.order_amount,dt_shop_order_goods.goods_title from dt_shop_orders
left join dt_shop_order_goods on dt_shop_orders.id = dt_shop_order_goods.order_id where dt_shop_orders.id = {0}", orderid);
DataTable dt = DbHelperSQL.Query(strSql).Tables[0];
Dictionary<string, string> biz_content_info = new Dictionary<string, string>();
biz_content_info.Add("timeout_express", "30m");//该笔订单允许的最晚付款时间,逾期将关闭交易。
biz_content_info.Add("seller_id", "");//收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
biz_content_info.Add("product_code", "QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
biz_content_info.Add("total_amount", dt.Rows[0]["order_amount"].ToString());//订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
biz_content_info.Add("subject", dt.Rows[0]["goods_title"].ToString() + "……");//商品的标题/交易标题/订单标题/订单关键字等。
biz_content_info.Add("body", dt.Rows[0]["order_no"].ToString());//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
biz_content_info.Add("out_trade_no", orderid);//商户网站唯一订单号
string strBizContent = JsonConvert.SerializeObject(biz_content_info, timejson);
return strBizContent;
}
/// <summary>
/// 组装普通文本请求参数(不带Encode)。
/// </summary>
/// <param name="parameters">Key-Value形式请求参数字典</param>
/// <returns>URL编码后的请求数据</returns>
public static string BuildQueryWithOutEncode(IDictionary<string, string> parameters)
{
StringBuilder postData = new StringBuilder();
bool hasParam = false;
IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();
while (dem.MoveNext())
{
string name = dem.Current.Key;
string value = dem.Current.Value;
// 忽略参数名或参数值为空的参数
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
{
if (hasParam)
{
postData.Append("&");
}
postData.Append(name);
postData.Append("=");
string encodedValue = value;
postData.Append(encodedValue);
hasParam = true;
}
}
return postData.ToString();
}
/// <summary>
/// 配置(请自行填上下面两个参数) 注释的部分代码不需要,只需要配置没有注释的4个基础参数
/// </summary>
public class Config
{
/// <summary>
/// 应用APPID
/// </summary>
public const string app_id = "自行填写";
/// <summary>
/// 私钥,通过工具生成 //这个就是生成器里面的那个私钥,第一个大框框那里的.
/// </summary>
/// rsa_private_key.pem 用于官方SDK获取支付宝支付信息
public const string privtekeyNocs8 = "自行填写";
//rsa_private_key_pkcs8.pem 用于获取支付宝支付信息
public const string privtekey = "自行填写";
//string APPID = "2017052207309344";
///// 私钥,通过工具生成 //这个就是生成器里面的那个私钥,第一个大框框那里的.
//rsa_private_key.pem 密钥
//string APP_PRIVATE_KEY = "自行填写";
//rsa_private_key_pkcs8.pem pkcs8密钥
//string APP_PRIVATE_KEY = "自行填写";
//配置支付宝的公钥 用于异步通知
public const string ALIPAY_PUBLIC_KEY = "自行填写";
//string CHARSET = "utf-8";
}
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace Common.RSAFromPkcs
{
/// <summary>
/// 类名:RSAFromPkcs8
/// 功能:RSA解密、签名、验签
/// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改
/// 版本:2.0
/// 修改日期:2011-05-10
/// 说明:
/// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
/// 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
/// </summary>
public sealed class RSAFromPkcs8
{
/// <summary>
/// 签名
/// </summary>
/// <param name="content">需要签名的内容</param>
/// <param name="privateKey">私钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns></returns>
public static string sign(string content, string privateKey, string input_charset)
{
Encoding code = Encoding.GetEncoding(input_charset);
byte[] Data = code.GetBytes(content);
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
byte[] signData = rsa.SignData(Data, sh);
return Convert.ToBase64String(signData);
}
/// <summary>
/// 验证签名
/// </summary>
/// <param name="content">需要验证的内容</param>
/// <param name="signedString">签名结果</param>
/// <param name="publicKey">公钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns></returns>
public static bool verify(string content, string signedString, string publicKey, string input_charset)
{
bool result = false;
Encoding code = Encoding.GetEncoding(input_charset);
byte[] Data = code.GetBytes(content);
byte[] data = Convert.FromBase64String(signedString);
RSAParameters paraPub = ConvertFromPublicKey(publicKey);
RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();
rsaPub.ImportParameters(paraPub);
SHA1 sh = new SHA1CryptoServiceProvider();
result = rsaPub.VerifyData(Data, sh, data);
return result;
}
/// <summary>
/// 用RSA解密
/// </summary>
/// <param name="resData">待解密字符串</param>
/// <param name="privateKey">私钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns>解密结果</returns>
public static string decryptData(string resData, string privateKey, string input_charset)
{
byte[] DataToDecrypt = Convert.FromBase64String(resData);
List<byte> result = new List<byte>();
for (int j = 0; j < DataToDecrypt.Length / 128; j++)
{
byte[] buf = new byte[128];
for (int i = 0; i < 128; i++)
{
buf[i] = DataToDecrypt[i + 128 * j];
}
result.AddRange(decrypt(buf, privateKey, input_charset));
}
byte[] source = result.ToArray();
char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];
Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);
return new string(asciiChars);
}
private static byte[] decrypt(byte[] data, string privateKey, string input_charset)
{
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
return rsa.Decrypt(data, false);
}
/// <summary>
/// 解析java生成的pem文件私钥
/// </summary>
/// <param name="pemstr"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)
{
byte[] pkcs8privatekey;
pkcs8privatekey = Convert.FromBase64String(pemstr);
if (pkcs8privatekey != null)
{
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
return rsa;
}
else
return null;
}
private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
{
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x02)
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0001)
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if (bt != 0x04) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
#region 解析.net 生成的Pem
private static RSAParameters ConvertFromPublicKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 162)
{
throw new ArgumentException("pem file content is incorrect.");
}
byte[] pemModulus = new byte[128];
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, 29, pemModulus, 0, 128);
Array.Copy(keyData, 159, pemPublicExponent, 0, 3);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
return para;
}
private static RSAParameters ConvertFromPrivateKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 609)
{
throw new ArgumentException("pem file content is incorrect.");
}
int index = 11;
byte[] pemModulus = new byte[128];
Array.Copy(keyData, index, pemModulus, 0, 128);
index += 128;
index += 2;//141
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, index, pemPublicExponent, 0, 3);
index += 3;
index += 4;//148
byte[] pemPrivateExponent = new byte[128];
Array.Copy(keyData, index, pemPrivateExponent, 0, 128);
index += 128;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279
byte[] pemPrime1 = new byte[64];
Array.Copy(keyData, index, pemPrime1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346
byte[] pemPrime2 = new byte[64];
Array.Copy(keyData, index, pemPrime2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413
byte[] pemExponent1 = new byte[64];
Array.Copy(keyData, index, pemExponent1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480
byte[] pemExponent2 = new byte[64];
Array.Copy(keyData, index, pemExponent2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546
byte[] pemCoefficient = new byte[64];
Array.Copy(keyData, index, pemCoefficient, 0, 64);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
para.D = pemPrivateExponent;
para.P = pemPrime1;
para.Q = pemPrime2;
para.DP = pemExponent1;
para.DQ = pemExponent2;
para.InverseQ = pemCoefficient;
return para;
}
#endregion
}
}
微信支付
#region 微信支付
[HttpPost, Route("OrderPay/GetWxPayInfo")]
public string GetWxPayInfo(dynamic obj)
{
string strJson = string.Empty;
try
{
string orderid = Convert.ToString(obj.orderid);
Model.dt_shop_orders orderEntity = new BLL.dt_shop_orders().GetModel(int.Parse(orderid));
if (orderEntity != null && orderEntity.id > 0)
{
decimal amount = orderEntity.order_amount;
string horderId = orderEntity.id.ToString();
string trade_no = orderEntity.order_no.ToString();
string hopenid = "";
string parameters = WxPayDataGet(amount, horderId, trade_no, hopenid, horderId);
return new Common.DotNetJson.JSONAPI().ToJson(parameters, "获取微信支付信息成功!", 0);
//JsonConvert.SerializeObject("{\"msg\":\"" + parameters + "\",\"type\":\"wxpay\"}"), "获取微信支付信息成功!", 0);
}
else
{
return new Common.DotNetJson.JSONAPI().ToJson(JsonConvert.SerializeObject("{\"error\":\"当前订单已失效!\"}"), "获取支付信息失败!", 1);
}
}
catch (Exception ex)
{
FileOperate.WriteLogInfo("", "GetPayInfo Exception:" + ex.Message);
return new Common.DotNetJson.JSONAPI().ToJson("{\"error\":\"" + ex.Message + "\"}", "获取微信支付信息失败!", 1);
}
}
/// <summary>
/// 微信支付:生成请求数据
/// </summary>
/// <param name="openid">微信用户id</openid>
/// <param name="ttFee">商品总价格</param>
/// <param name="busiBody"></param>
/// <returns></returns>
protected string WxPayDataGet(decimal ttFee, string busiBody, string out_trade_no, string openid, string otid)
{
//BLL.wx_payment_wxpay wxPayBll = new BLL.wx_payment_wxpay();
//Model.wx_payment_wxpay paymentInfo = wxPayBll.GetModelByWid(wid);
//先设置基本信息
//string partnerId = ""; // paymentInfo.partnerId;// " ";//
//string appId = ""; // paymentInfo.appId;// " ";//
//string partnerKey = ""; // paymentInfo.partnerKey;// " ";//
////paysignkey(非appkey)
//string appKey = ""; // paymentInfo.paySignKey;
//--
JsApiPay jsApiPay = new JsApiPay();
jsApiPay.openid = openid;
jsApiPay.total_fee = int.Parse(((int)(ttFee * 100)).ToString());
string attch = 3 + "|" + otid + "|我爱我县";
WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult(openid, otid, attch, "woaiwoxian");
WxPayData jsApiParam = new WxPayData();
string time = WxPayApi.GenerateTimeStamp();
string nonceStr = WxPayApi.GenerateNonceStr();
string pack = "prepay_id=" + unifiedOrderResult.GetValue("prepay_id");
//**************************************************封装调起微信客户端支付界面字符串********************
//设置待加密支付参数并加密
string appid = unifiedOrderResult.GetValue("appid").ToString();
string mch_Id = unifiedOrderResult.GetValue("mch_id").ToString();
string prepay_id = unifiedOrderResult.GetValue("prepay_id").ToString();
jsApiParam.SetValue("appId", appid); //
jsApiParam.SetValue("partnerid", mch_Id);
jsApiParam.SetValue("prepayid", prepay_id);
jsApiParam.SetValue("package", "Sign=WXPay");
jsApiParam.SetValue("nonceStr", nonceStr);
jsApiParam.SetValue("timeStamp", time);
string sign = jsApiParam.MakeSign();
// jsApiParam.SetValue("sign", sign);
//设置支付包参数
WxPayData paydata = new WxPayData();
paydata.SetValue("retcode", 0);//5+固定调起参数
paydata.SetValue("retmsg", "ok");//5+固定调起参数
paydata.SetValue("appid", appid);//AppId,微信开放平台新建应用时产生
paydata.SetValue("partnerid", mch_Id);//商户编号,微信开放平台申请微信支付时产生
paydata.SetValue("prepayid", prepay_id);//由上面获取预支付流程获取
paydata.SetValue("package", "Sign=WXpay");//APP支付固定设置参数
paydata.SetValue("noncestr", nonceStr);//随机字符串,
paydata.SetValue("timestamp", time);//时间戳
Hashtable paySignReqHandler = new Hashtable();
paySignReqHandler.Add("appid", appid);
paySignReqHandler.Add("partnerid", mch_Id);
paySignReqHandler.Add("prepayid", prepay_id);
paySignReqHandler.Add("noncestr", nonceStr);
paySignReqHandler.Add("package", "Sign=WXpay");
paySignReqHandler.Add("timestamp", time.ToString());
var paySign = CreateMd5Sign(paySignReqHandler);
paydata.SetValue("sign", paySign);//时间戳
//var serializer = new JavaScriptSerializer();
//var result = serializer.Serialize(obj);
//paySign = HttpUtility.UrlEncode(paySign, Encoding.UTF8);
//string payInfo = GetOrderInfoWithEncode() + "&sign=" + sign;
//strJson = payInfo.Replace("+", "%20");//日期那里会有一个空格(2017-01-05 11:11:11)转化为+,所以这里要替换一下
// FileOperate.WriteLogInfo("", "支付宝串:" + strJson);
string result = paydata.ToJson();
// FileOperate.WriteLogInfo("", "result:" + result);
return result;
}
private string CreateMd5Sign(Hashtable parameters)
{
var sb = new StringBuilder();
var akeys = new ArrayList(parameters.Keys);
akeys.Sort();//排序,这是微信要求的
foreach (string k in akeys)
{
var v = (string)parameters[k];
sb.Append(k + "=" + v + "&");
}
sb.Append("key=" + WxPayAPI.WxPayConfig.KEY);
string sign = GetMD5(sb.ToString());
return sign;
}
private string GetMD5(string src)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] data = Encoding.UTF8.GetBytes(src);
byte[] md5data = md5.ComputeHash(data);
md5.Clear();
var retStr = BitConverter.ToString(md5data);
retStr = retStr.Replace("-", "").ToUpper();
return retStr;
}
#endregion
//=========================================================================
//微信支付demo里面的部分代码 JsApiPay ,其他的代码基本在微信sdk demo里面基本完成,细节部分自己待处理就好了
/**
* 调用统一下单,获得下单结果
* @return 统一下单结果
* @失败时抛异常WxPayException
*/
public WxPayData GetUnifiedOrderResult(string hasopenid, string body, string attach, string tag)
{
string strSql = string.Format(@"select dt_shop_orders.id,dt_shop_orders.order_no,dt_shop_orders.order_amount,dt_shop_order_goods.goods_title from dt_shop_orders
left join dt_shop_order_goods on dt_shop_orders.id = dt_shop_order_goods.order_id where dt_shop_orders.id = {0}", body);
DataTable dt = DbHelperSQL.Query(strSql).Tables[0];
//统一下单
WxPayData data = new WxPayData();
data.SetValue("body", dt.Rows[0]["goods_title"].ToString() + "...");
data.SetValue("attach", attach);
data.SetValue("out_trade_no", body);
data.SetValue("total_fee", total_fee);
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
data.SetValue("goods_tag", tag);
data.SetValue("trade_type", "APP");
data.SetValue("openid", hasopenid);
//异步通知url未设置,则使用配置文件中的url
data.SetValue("notify_url", "http://异步通知url/OrderPay/WXUpdateStatus");//异步通知url OrderPay/WXUpdateStatus
data.SetValue("appid", WxPayConfig.APPID);//公众账号ID
data.SetValue("mch_id", WxPayConfig.MCHID);//商户号
data.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
//签名
data.SetValue("sign", data.MakeSign());
WxPayData result = WxPayApi.UnifiedOrder(data);
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
{
Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
throw new WxPayException("UnifiedOrder response error!");
}
unifiedOrderResult = result;
return result;
}
支付宝
Android&iOS
错误码 错误描述
8000 正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
4000 订单支付失败
5000 重复请求
6001 用户中途取消
6002 网络连接出错
6004 支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
微信支付
Android&iOS
错误码 错误描述
-1 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等
-2 无需处理。发生场景:用户不支付了,点击取消,返回APP。
-3 发送失败
-4 授权失败
-5 微信不支持
-
最后说明: 支付宝支付签名是 rsa_private_key_pkcs8 这个方式,千万不搞错了,本文使用是RSA,不是RSA2,然后支付宝 公钥 私钥不要搞错了 ,支付宝支付支持在线调试,微信不支持,需要打包安装才能测试支付功能,
微信支付还有一个点,密钥需要一致,不能错误!
由于时间匆忙,贴了一些代码,需要各位同学自行完善和修改,如有错误,欢迎指出我来及时修改完善,其他问题, 欢迎加群来咨询交流学习!谢谢大家
收起阅读 »
plus.maps.Map("xxx").getUserLocation VS plus.geolocation.getCurrentPosition
项目中一开始混用了
getUserLocation
和
getCurrentPosition
###############
官方文档写的很不透彻,我在开发的过程中也是用的模棱两可。
写这篇文章是自己开发过程的一个记录,也是功能迭代优化的不断尝试。
希望最终能把他两说清楚。
###############
前提是配置好地图SDK需要的appkey;
混用的原因是:
- 不但需要准确的地理坐标,还需要具体省市区街道信息
- getUserLocation得到的坐标经过reverseGeocode反向地理编码拿到的address是String,信息量很少
- getCurrentPosition得到的address直接就是Object:country、province、city、district、street、poiName、cityCode。
这种数据用起来更方便。
同时还有String格式的addresses属性,基本可以理解为address对象中地址属性串联,但也不是机械的串联,中间存在有语意的形容词。
- 真机调试时getCurrentPosition取到的地理坐标偏差非常大,2km的级别。而getUserLocation相对比较准确。
这里有个大坑
getCurrentPosition优先使用的定位模块:amap>baidu>system,来自HTML5 官方文档
真机调试默认baidu地图,baidu地图默认bd09ll坐标系,而getCurrentPosition不设置参数的话默认amap是gcj02坐标系!!!
偏差不大就见鬼了!没有偏差gcj02岂不是搞了个国际笑话!
有偏差才正常!
HBuilder这个默认不一致的坑把我坑坏了
最后做性能和代码优化的时候,觉得这种做法有问题,不够优雅!就仔细研究了他两的区别。
总结如下:
- 真机调试虽然是通过手机启动服务,但是实际上使用的是HBuilder默认的配置
- 默认使用的地图provider是baidu,并且getUserLocation和reverseGeocode方法的coordType默认是bd09ll。
并且是不可以修改的!想用amap调试,只能一遍一遍的打包安装! - getCurrentPosition定位实际上是通过GPS定位,缺点是如果在室内使用4G网络,定位非常不准,使用WiFi比较准。
如果室外4G GPS的精度是最高的。这里要吐槽下,baidu的准确度木有amap高。 - 从HTML5 官方文档看,getCurrentPosition通过手机GPS设备或其它信息如IP地址、移动网络信号获取。具体优先级策略不详。
getUserLocation只有一句:获取用户当前位置信息将打开定位设备。
getCurrentPosition默认采用amap,是gcj02坐标系,getUserLocation采用baidu的bd09ll坐标系。
通过测试发现:
- 如果要使用getCurrentPosition,不论sdk是baidu还是amap,一定要注意设置对应的provider和coordsType。
- 4G网络下,getCurrentPosition一定优先通过GPS定位,如果在室内就误差很大,定位非常不准。
- 室内使用WiFi相比4G而言要准一些
- WIFI就是IP地址或基站定位,准确度完全依赖于数据量,即调用得越多越准确。
- getUserLocation应该是基于GPS定位,没有验证出来会不会使用基站。关键缺点是能获取到的信息量太少。
- 我最终采用了getCurrentPosition来确定当前位置,毕竟getCurrentPosition可以自由切换定位方式,其次现在wifi覆盖率非常高,基于位置的服务越来越多,用的人越来越多,基站定位可靠度也会越来越高。
补充:
- 坐标转换为什么不用convertCoordinates?
因为只有百度地图支持这个方法,我不希望被绑架。
后续测试有新发现了继续补充。
项目中一开始混用了
getUserLocation
和
getCurrentPosition
###############
官方文档写的很不透彻,我在开发的过程中也是用的模棱两可。
写这篇文章是自己开发过程的一个记录,也是功能迭代优化的不断尝试。
希望最终能把他两说清楚。
###############
前提是配置好地图SDK需要的appkey;
混用的原因是:
- 不但需要准确的地理坐标,还需要具体省市区街道信息
- getUserLocation得到的坐标经过reverseGeocode反向地理编码拿到的address是String,信息量很少
- getCurrentPosition得到的address直接就是Object:country、province、city、district、street、poiName、cityCode。
这种数据用起来更方便。
同时还有String格式的addresses属性,基本可以理解为address对象中地址属性串联,但也不是机械的串联,中间存在有语意的形容词。
- 真机调试时getCurrentPosition取到的地理坐标偏差非常大,2km的级别。而getUserLocation相对比较准确。
这里有个大坑
getCurrentPosition优先使用的定位模块:amap>baidu>system,来自HTML5 官方文档
真机调试默认baidu地图,baidu地图默认bd09ll坐标系,而getCurrentPosition不设置参数的话默认amap是gcj02坐标系!!!
偏差不大就见鬼了!没有偏差gcj02岂不是搞了个国际笑话!
有偏差才正常!
HBuilder这个默认不一致的坑把我坑坏了
最后做性能和代码优化的时候,觉得这种做法有问题,不够优雅!就仔细研究了他两的区别。
总结如下:
- 真机调试虽然是通过手机启动服务,但是实际上使用的是HBuilder默认的配置
- 默认使用的地图provider是baidu,并且getUserLocation和reverseGeocode方法的coordType默认是bd09ll。
并且是不可以修改的!想用amap调试,只能一遍一遍的打包安装! - getCurrentPosition定位实际上是通过GPS定位,缺点是如果在室内使用4G网络,定位非常不准,使用WiFi比较准。
如果室外4G GPS的精度是最高的。这里要吐槽下,baidu的准确度木有amap高。 - 从HTML5 官方文档看,getCurrentPosition通过手机GPS设备或其它信息如IP地址、移动网络信号获取。具体优先级策略不详。
getUserLocation只有一句:获取用户当前位置信息将打开定位设备。
getCurrentPosition默认采用amap,是gcj02坐标系,getUserLocation采用baidu的bd09ll坐标系。
通过测试发现:
- 如果要使用getCurrentPosition,不论sdk是baidu还是amap,一定要注意设置对应的provider和coordsType。
- 4G网络下,getCurrentPosition一定优先通过GPS定位,如果在室内就误差很大,定位非常不准。
- 室内使用WiFi相比4G而言要准一些
- WIFI就是IP地址或基站定位,准确度完全依赖于数据量,即调用得越多越准确。
- getUserLocation应该是基于GPS定位,没有验证出来会不会使用基站。关键缺点是能获取到的信息量太少。
- 我最终采用了getCurrentPosition来确定当前位置,毕竟getCurrentPosition可以自由切换定位方式,其次现在wifi覆盖率非常高,基于位置的服务越来越多,用的人越来越多,基站定位可靠度也会越来越高。
补充:
- 坐标转换为什么不用convertCoordinates?
因为只有百度地图支持这个方法,我不希望被绑架。
后续测试有新发现了继续补充。
收起阅读 »
诚接App开发,可能是目前国内唯一一家能提供完整技术支撑服务的公司
默客科技 (http://www.mokekeji.com) 专业跨平台App开发,提供完整技术支撑服务。
之所以说可能是唯一一家,是因为国内确实没看见其他公司有类似的服务,如果有类似的,望在下方回帖告悉,我看到后就会修改标题。
什么叫做技术支撑?放两张截图就一目了然:
仿github的issue tracker系统,真正快速有效的解决问题。
得益于架构的跨平台版本管理和部署系统,测试或发布都可以一键操作,一目了然。
外包的项目在上线后,会面临大量的问题,比如如何快速修正bug,如何升级迭代,如何进行测试和发布等等。
传统的做法无非是通过各种交流工具催来催去,直到某一方筋疲力尽,然后项目要么停滞,要么重构,我们已经见过无数的案例。
所谓技术支撑,必须有能力替代自身的开发团队。
技术架构
大约一个月前我们在这里发了一篇文章,介绍技术架构和和平台选择的原因 (https://ask.dcloud.net.cn/article/12567),帖子下获得了dcloud官方人员的一些见解,因为话题比较大,所以没有直接回复,这里简述下个人看法。
随着移动设备性能的逐渐提升,原生应用和混合式应用的性能差距将越来越小,所以性能上的考量不再讨论,但是RN或Weex与h5应用间有一个巨大的不同,h5的开发架构过于松散了,它没有强制的开发规范和架构约束。诚然你可以使用react或者vue来开发应用,但Jq仍然在前端领域占绝对优势,现在的大多数前端开发人员可能直到离开这个行业都只会Jq。就像ie一样,除非所有的网站都不再支持ie,否则ie仍然会长期存在下去。
Jq并不是不好,但Jq与mvvm框架对于应用开发的理解方式是完全不同的,这种不同会在项目的可维护性上体现出巨大的差距,一个写Jq五年的工程师可能没有一个写Vue一年的工程师来的bug更少,代码思路更清晰,更容易维护,君不见Github上有许多star非常高的Jq插件仍然是bug满天飞。
强规范架构非常重要,我们始终在贯彻这点。
开发中很常见的一件事,为了实现一个小功能,找了无数插件,终于选定一个后,项目迭代更新,原来的插件不够用了,又要继续找下去。
一个项目会用到很多插件,这些插件很可能没有完整的文档,没有统一的api,这又再次增加了维护的难度。
所以我们专门维护了一套组件库,其中多数是自己开发,少数是封装一些广受认可的组件,然后在项目中特定的功能强制要求使用特定组件的来实现,包括所有页面布局,div + css在这里已经被淘汰。当功能需求变更时,只需要升级对应的组件即可。只有这样才能让开发人员将精力放到业务逻辑上,而不是在一些旁枝末节的地方。
我们致力于开发高质量的跨平台应用,业务联系请参考官网。
http://www.mokekeji.com
默客科技 (http://www.mokekeji.com) 专业跨平台App开发,提供完整技术支撑服务。
之所以说可能是唯一一家,是因为国内确实没看见其他公司有类似的服务,如果有类似的,望在下方回帖告悉,我看到后就会修改标题。
什么叫做技术支撑?放两张截图就一目了然:
仿github的issue tracker系统,真正快速有效的解决问题。
得益于架构的跨平台版本管理和部署系统,测试或发布都可以一键操作,一目了然。
外包的项目在上线后,会面临大量的问题,比如如何快速修正bug,如何升级迭代,如何进行测试和发布等等。
传统的做法无非是通过各种交流工具催来催去,直到某一方筋疲力尽,然后项目要么停滞,要么重构,我们已经见过无数的案例。
所谓技术支撑,必须有能力替代自身的开发团队。
技术架构
大约一个月前我们在这里发了一篇文章,介绍技术架构和和平台选择的原因 (https://ask.dcloud.net.cn/article/12567),帖子下获得了dcloud官方人员的一些见解,因为话题比较大,所以没有直接回复,这里简述下个人看法。
随着移动设备性能的逐渐提升,原生应用和混合式应用的性能差距将越来越小,所以性能上的考量不再讨论,但是RN或Weex与h5应用间有一个巨大的不同,h5的开发架构过于松散了,它没有强制的开发规范和架构约束。诚然你可以使用react或者vue来开发应用,但Jq仍然在前端领域占绝对优势,现在的大多数前端开发人员可能直到离开这个行业都只会Jq。就像ie一样,除非所有的网站都不再支持ie,否则ie仍然会长期存在下去。
Jq并不是不好,但Jq与mvvm框架对于应用开发的理解方式是完全不同的,这种不同会在项目的可维护性上体现出巨大的差距,一个写Jq五年的工程师可能没有一个写Vue一年的工程师来的bug更少,代码思路更清晰,更容易维护,君不见Github上有许多star非常高的Jq插件仍然是bug满天飞。
强规范架构非常重要,我们始终在贯彻这点。
开发中很常见的一件事,为了实现一个小功能,找了无数插件,终于选定一个后,项目迭代更新,原来的插件不够用了,又要继续找下去。
一个项目会用到很多插件,这些插件很可能没有完整的文档,没有统一的api,这又再次增加了维护的难度。
所以我们专门维护了一套组件库,其中多数是自己开发,少数是封装一些广受认可的组件,然后在项目中特定的功能强制要求使用特定组件的来实现,包括所有页面布局,div + css在这里已经被淘汰。当功能需求变更时,只需要升级对应的组件即可。只有这样才能让开发人员将精力放到业务逻辑上,而不是在一些旁枝末节的地方。
我们致力于开发高质量的跨平台应用,业务联系请参考官网。
http://www.mokekeji.com

顶部选项卡-可左右拖动(webview):用这个的时候webviewGroup 出现一个页面无法打开,只有点返回,才会出现页面。
顶部选项卡-可左右拖动(webview)
tab-top-webview-main.html
问题:用这个的时候webviewGroup 出现一个页面无法打开,只有点返回,才会出现页面。
解决方法:
var group = new webviewGroup("viewgroup", {
换为
var group = new webviewGroup(plus.webview.currentWebview().id,{
顶部选项卡-可左右拖动(webview)
tab-top-webview-main.html
问题:用这个的时候webviewGroup 出现一个页面无法打开,只有点返回,才会出现页面。
解决方法:
var group = new webviewGroup("viewgroup", {
换为
var group = new webviewGroup(plus.webview.currentWebview().id,{

使用阿里云图标做的漂亮九宫格
使用阿里云图标做的漂亮九宫格
<ul class="mui-table-view mui-grid-view mui-grid-9">
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-tianmao"></span>
<div class="mui-media-body">天猫</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-waimai1"><span class="mui-badge">5</span></span>
<div class="mui-media-body">外卖</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-juhuasuan"></span>
<div class="mui-media-body">聚划算</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-chong"></span>
<div class="mui-media-body">充值</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-feizhulvhang"></span>
<div class="mui-media-body">飞猪旅行</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-class" ></span>
<div class="mui-media-body">分类</div>
</a>
</li>
</ul>
使用阿里云图标做的漂亮九宫格
<ul class="mui-table-view mui-grid-view mui-grid-9">
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-tianmao"></span>
<div class="mui-media-body">天猫</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-waimai1"><span class="mui-badge">5</span></span>
<div class="mui-media-body">外卖</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-juhuasuan"></span>
<div class="mui-media-body">聚划算</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-chong"></span>
<div class="mui-media-body">充值</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-feizhulvhang"></span>
<div class="mui-media-body">飞猪旅行</div>
</a>
</li>
<li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3">
<a href="#">
<span class="mui-icon iconfont icon-class" ></span>
<div class="mui-media-body">分类</div>
</a>
</li>
</ul>
收起阅读 »

mui中美丽的popover
新手 在mui中找不到我想要的弹框类型的模拟窗口,自己改了一下
<div id="chosesku" style="background: #FFFFFF;" class="chosesku box mui-popover mui-popover-action mui-popover-bottom">
<div class="choseskuimg">
<img style=" border-radius: 5px; width: 70px ;height: 90px;" src="../images/shuijiao.jpg" />
</div>
<div style="margin-top: 25px; margin-left: 90px;">
<h4 style="color: red;">¥9999</h4>
<h5>商品号:44050505</h5>
</div>
<div class="solid"></div>
<!--sku选项 -->
<div>
<h5 style="margin-left: 5px;margin-top: 10px;">颜色</h5>
<input class="sku" type="button" value="白色" />
<input class="sku" type="button" value="红色" />
<input class="sku" type="button" value="黑色" />
<input class="sku" type="button" value="灰色" />
<input class="sku" type="button" value="灰白色" />
</div>
<div>
<h5 style="margin-left: 5px;margin-top: 10px;">体重</h5>
<input class="sku" type="button" value="1kg" />
<input class="sku" type="button" value="1.5kg" />
<input class="sku" type="button" value="1.8kg" />
</div>
<div class="solid"></div>
<div style="margin-top: 10px;">
<h5 style="margin-left: 5px;margin-top: 10px;display: block;float: left;">数量</h5>
<div class="mui-numbox " style="width: 130px;height: 33px;float: right;" data-numbox-min='1' data-numbox-max='99'>
<button class="mui-btn mui-btn-numbox-minus" type="button">-</button>
<input id="id_item_count" class="mui-input-numbox" type="number" />
<button class="mui-btn mui-btn-numbox-plus" type="button">+</button>
</div>
</div>
<div class="footer">
<button type="button" class="mui-btn mui-btn-warning mui-btn-block">加入购物车</button>
</div>
</div>
点击弹出
<a href="#chosesku">
<div style="margin-top: 10px; border-top: solid 0.5px #E8E8E8;border-bottom: solid 0.5px #E8E8E8;height: 55px;">
<p style="float:left">已选:</p>
<h5 style="">白喵,灰白,官方标配!白喵,</h5>
<div style="float: right;margin-top: -20px;font-size: 30px;margin-right: 10px;">…</div>
</div>
</a>
新手 在mui中找不到我想要的弹框类型的模拟窗口,自己改了一下
<div id="chosesku" style="background: #FFFFFF;" class="chosesku box mui-popover mui-popover-action mui-popover-bottom">
<div class="choseskuimg">
<img style=" border-radius: 5px; width: 70px ;height: 90px;" src="../images/shuijiao.jpg" />
</div>
<div style="margin-top: 25px; margin-left: 90px;">
<h4 style="color: red;">¥9999</h4>
<h5>商品号:44050505</h5>
</div>
<div class="solid"></div>
<!--sku选项 -->
<div>
<h5 style="margin-left: 5px;margin-top: 10px;">颜色</h5>
<input class="sku" type="button" value="白色" />
<input class="sku" type="button" value="红色" />
<input class="sku" type="button" value="黑色" />
<input class="sku" type="button" value="灰色" />
<input class="sku" type="button" value="灰白色" />
</div>
<div>
<h5 style="margin-left: 5px;margin-top: 10px;">体重</h5>
<input class="sku" type="button" value="1kg" />
<input class="sku" type="button" value="1.5kg" />
<input class="sku" type="button" value="1.8kg" />
</div>
<div class="solid"></div>
<div style="margin-top: 10px;">
<h5 style="margin-left: 5px;margin-top: 10px;display: block;float: left;">数量</h5>
<div class="mui-numbox " style="width: 130px;height: 33px;float: right;" data-numbox-min='1' data-numbox-max='99'>
<button class="mui-btn mui-btn-numbox-minus" type="button">-</button>
<input id="id_item_count" class="mui-input-numbox" type="number" />
<button class="mui-btn mui-btn-numbox-plus" type="button">+</button>
</div>
</div>
<div class="footer">
<button type="button" class="mui-btn mui-btn-warning mui-btn-block">加入购物车</button>
</div>
</div>
点击弹出
<a href="#chosesku">
<div style="margin-top: 10px; border-top: solid 0.5px #E8E8E8;border-bottom: solid 0.5px #E8E8E8;height: 55px;">
<p style="float:left">已选:</p>
<h5 style="">白喵,灰白,官方标配!白喵,</h5>
<div style="float: right;margin-top: -20px;font-size: 30px;margin-right: 10px;">…</div>
</div>
</a>

控制wifi
/*
仅支持android
*/
window.WIFI = function() {
this.init();
}
/**
-
初始化
*/
WIFI.prototype.init = function() {
var Context = plus.android.importClass("android.content.Context");
plus.android.importClass("android.net.wifi.WifiManager");
plus.android.importClass("java.util.List");
plus.android.importClass("java.util.ArrayList");
plus.android.importClass("android.net.wifi.ScanResult");
plus.android.importClass("android.net.wifi.WifiInfo");
plus.android.importClass("java.util.BitSet");this.WifiConfiguration = plus.android.importClass("android.net.wifi.WifiConfiguration");
this.wifiManager = plus.android.runtimeMainActivity().getSystemService(Context.WIFI_SERVICE);
}
/**
- 获取wifi列表
*/
WIFI.prototype.getAllList = function() {
return this.wifis = this.wifiManager.getScanResults();
}
/**
- 校验ssid 返回的是有此ssid的wifi个数
-
@param {Object} ssid wifi名
*/
WIFI.prototype.checkSsid = function(ssid) {
var list = this.wifiManager.getScanResults();
var len = list.size();
var num = 0;
for(var i = 0; i < len; i++) {
var tmp = list.get(i);if(tmp.plusGetAttribute('SSID') == ssid) { console.log('checkSsid === ' + i + '>>' + tmp.plusGetAttribute('SSID')) num++; }
}
return num;
}
/**
- 移除已经存在的ssid
- @param {Object} ssid wifi名
*/
WIFI.prototype.removeExsits = function(ssid) {
var list = this.wifiManager.getConfiguredNetworks(); //获取到已经配置过的wifi列表
var len = list.size();
var wifiInfo = this.getNow();
var tSsid = '"' + ssid + '"';
for(var i = 0; i < len; i++) {
var tmp = list.get(i);
var tmpSsid = tmp.plusGetAttribute('SSID');
console.log('removeExsits===>' + tmpSsid);
if(tmpSsid == tSsid) {
console.log('removeExsits===>' + tmpSsid);
this.disConnect(wifiInfo.getNetworkId());
this.wifiManager.removeNetwork(tmp.plusGetAttribute('networkId'));
}
}
}
/**
- 连接已有的wifi 会自动校验
- @param {Object} netWorkId wifi的id
*/
WIFI.prototype.connectOld = function(netWorkId) {
var now = this.getNow();
if(now.getNetworkId() != netWorkId) { //当前连接的不是将要连接的
this.wifiManager.enableNetwork(netWorkId, true);
}
}
/**
- 取消连接
- @param {Object} netWorkId wifi的id
*/
WIFI.prototype.disConnect = function(netWorkId) {
var now = this.getNow();
if(now.getNetworkId() == netWorkId) { //当前连接的是需要取消的 则取消
this.wifiManager.disableNetwork(netWorkId);
this.wifiManager.disconnect();
}
}
/**
- 获取当前连接的wifi
*/
WIFI.prototype.getNow = function() {
return this.wifiManager.getConnectionInfo();
}
/** -
*/
WIFI.prototype.getWifiState = function() {
return this.wifiManager.getWifiState();
}
/**
- 添加新的wifi并连接
- @param {Object} ssid wifi名
-
@param {Object} pwd 密码
*/
WIFI.prototype.connectNew = function(ssid, pwd) {
var WifiConfiguration = this.WifiConfiguration;
var wcf = new WifiConfiguration();wcf.plusGetAttribute('allowedAuthAlgorithms').set(WifiConfiguration.AuthAlgorithm.OPEN);
wcf.plusGetAttribute('allowedGroupCiphers').set(WifiConfiguration.GroupCipher.TKIP);
wcf.plusGetAttribute('allowedKeyManagement').set(WifiConfiguration.KeyMgmt.WPA_PSK);
wcf.plusGetAttribute('allowedPairwiseCiphers').set(WifiConfiguration.PairwiseCipher.TKIP);
wcf.plusGetAttribute('allowedGroupCiphers').set(WifiConfiguration.GroupCipher.CCMP);
wcf.plusGetAttribute('allowedPairwiseCiphers').set(WifiConfiguration.PairwiseCipher.CCMP);wcf.plusSetAttribute('status', WifiConfiguration.Status.ENABLED);
wcf.plusSetAttribute('SSID', '"' + ssid + '"');
wcf.plusSetAttribute('preSharedKey', '"' + pwd + '"');
wcf.plusSetAttribute('hiddenSSID', true);var wcgID = this.wifiManager.addNetwork(wcf);
var b = this.wifiManager.enableNetwork(wcgID, true);
}
/**
- 改变连接的wifi
- @param {Object} index wifi列表的索引
*/
WIFI.prototype.change = function(index) {
// 索引大于配置好的网络索引返回
if(index > this.wifis.size()) {
return;
}
// 连接配置好的指定ID的网络
this.wifiManager.enableNetwork(this.wifis.get(index).plusGetAttribute('networkId'), true);
}
/**
- 获取wifi是否打开
*/
WIFI.prototype.isWifiEnabled = function() {
return this.wifiManager.isWifiEnabled();
}
/**
- 打开wifi
*/
WIFI.prototype.open = function() {
console.log('open函数已执行');
this.wifiManager.setWifiEnabled(true);
}
/**
- 关闭wifi
*/
WIFI.prototype.close = function() {
this.wifiManager.setWifiEnabled(false);
}
/**
- 兼容版的wifi设置 只提供跳转wifi设置界面
*/
window.C_WIFI = function() {
this.main = plus.android.runtimeMainActivity();
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass('android.provider.Settings');
this.wifiIntent = new Intent(Settings.ACTION_SETTINGS);
}
/** - 跳转wifi界面
*/
C_WIFI.prototype.goWifi = function() {
this.main.startActivity(this.wifiIntent);
}
/*
仅支持android
*/
window.WIFI = function() {
this.init();
}
/**
-
初始化
*/
WIFI.prototype.init = function() {
var Context = plus.android.importClass("android.content.Context");
plus.android.importClass("android.net.wifi.WifiManager");
plus.android.importClass("java.util.List");
plus.android.importClass("java.util.ArrayList");
plus.android.importClass("android.net.wifi.ScanResult");
plus.android.importClass("android.net.wifi.WifiInfo");
plus.android.importClass("java.util.BitSet");this.WifiConfiguration = plus.android.importClass("android.net.wifi.WifiConfiguration");
this.wifiManager = plus.android.runtimeMainActivity().getSystemService(Context.WIFI_SERVICE);
}
/**
- 获取wifi列表
*/
WIFI.prototype.getAllList = function() {
return this.wifis = this.wifiManager.getScanResults();
}
/**
- 校验ssid 返回的是有此ssid的wifi个数
-
@param {Object} ssid wifi名
*/
WIFI.prototype.checkSsid = function(ssid) {
var list = this.wifiManager.getScanResults();
var len = list.size();
var num = 0;
for(var i = 0; i < len; i++) {
var tmp = list.get(i);if(tmp.plusGetAttribute('SSID') == ssid) { console.log('checkSsid === ' + i + '>>' + tmp.plusGetAttribute('SSID')) num++; }
}
return num;
}
/**
- 移除已经存在的ssid
- @param {Object} ssid wifi名
*/
WIFI.prototype.removeExsits = function(ssid) {
var list = this.wifiManager.getConfiguredNetworks(); //获取到已经配置过的wifi列表
var len = list.size();
var wifiInfo = this.getNow();
var tSsid = '"' + ssid + '"';
for(var i = 0; i < len; i++) {
var tmp = list.get(i);
var tmpSsid = tmp.plusGetAttribute('SSID');
console.log('removeExsits===>' + tmpSsid);
if(tmpSsid == tSsid) {
console.log('removeExsits===>' + tmpSsid);
this.disConnect(wifiInfo.getNetworkId());
this.wifiManager.removeNetwork(tmp.plusGetAttribute('networkId'));
}
}
}
/**
- 连接已有的wifi 会自动校验
- @param {Object} netWorkId wifi的id
*/
WIFI.prototype.connectOld = function(netWorkId) {
var now = this.getNow();
if(now.getNetworkId() != netWorkId) { //当前连接的不是将要连接的
this.wifiManager.enableNetwork(netWorkId, true);
}
}
/**
- 取消连接
- @param {Object} netWorkId wifi的id
*/
WIFI.prototype.disConnect = function(netWorkId) {
var now = this.getNow();
if(now.getNetworkId() == netWorkId) { //当前连接的是需要取消的 则取消
this.wifiManager.disableNetwork(netWorkId);
this.wifiManager.disconnect();
}
}
/**
- 获取当前连接的wifi
*/
WIFI.prototype.getNow = function() {
return this.wifiManager.getConnectionInfo();
}
/** -
*/
WIFI.prototype.getWifiState = function() {
return this.wifiManager.getWifiState();
}
/**
- 添加新的wifi并连接
- @param {Object} ssid wifi名
-
@param {Object} pwd 密码
*/
WIFI.prototype.connectNew = function(ssid, pwd) {
var WifiConfiguration = this.WifiConfiguration;
var wcf = new WifiConfiguration();wcf.plusGetAttribute('allowedAuthAlgorithms').set(WifiConfiguration.AuthAlgorithm.OPEN);
wcf.plusGetAttribute('allowedGroupCiphers').set(WifiConfiguration.GroupCipher.TKIP);
wcf.plusGetAttribute('allowedKeyManagement').set(WifiConfiguration.KeyMgmt.WPA_PSK);
wcf.plusGetAttribute('allowedPairwiseCiphers').set(WifiConfiguration.PairwiseCipher.TKIP);
wcf.plusGetAttribute('allowedGroupCiphers').set(WifiConfiguration.GroupCipher.CCMP);
wcf.plusGetAttribute('allowedPairwiseCiphers').set(WifiConfiguration.PairwiseCipher.CCMP);wcf.plusSetAttribute('status', WifiConfiguration.Status.ENABLED);
wcf.plusSetAttribute('SSID', '"' + ssid + '"');
wcf.plusSetAttribute('preSharedKey', '"' + pwd + '"');
wcf.plusSetAttribute('hiddenSSID', true);var wcgID = this.wifiManager.addNetwork(wcf);
var b = this.wifiManager.enableNetwork(wcgID, true);
}
/**
- 改变连接的wifi
- @param {Object} index wifi列表的索引
*/
WIFI.prototype.change = function(index) {
// 索引大于配置好的网络索引返回
if(index > this.wifis.size()) {
return;
}
// 连接配置好的指定ID的网络
this.wifiManager.enableNetwork(this.wifis.get(index).plusGetAttribute('networkId'), true);
}
/**
- 获取wifi是否打开
*/
WIFI.prototype.isWifiEnabled = function() {
return this.wifiManager.isWifiEnabled();
}
/**
- 打开wifi
*/
WIFI.prototype.open = function() {
console.log('open函数已执行');
this.wifiManager.setWifiEnabled(true);
}
/**
- 关闭wifi
*/
WIFI.prototype.close = function() {
this.wifiManager.setWifiEnabled(false);
}
/**
- 兼容版的wifi设置 只提供跳转wifi设置界面
*/
window.C_WIFI = function() {
this.main = plus.android.runtimeMainActivity();
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass('android.provider.Settings');
this.wifiIntent = new Intent(Settings.ACTION_SETTINGS);
}
/** - 跳转wifi界面
*/
C_WIFI.prototype.goWifi = function() {
this.main.startActivity(this.wifiIntent);
}

微信分享图片,在苹果上无效怎办呀?
同样的代码,
在安桌上是可以正常分享的!
在苹果上就分享不了!
sharewx.send( {pictures:[sd_path],extra:{scene:"WXSceneSession"}}, function(){
plus.nativeUI.alert( "分享成功!" );
}, function(e){
plus.nativeUI.alert( "分享失败!");
});
同样的代码,
在安桌上是可以正常分享的!
在苹果上就分享不了!
sharewx.send( {pictures:[sd_path],extra:{scene:"WXSceneSession"}}, function(){
plus.nativeUI.alert( "分享成功!" );
}, function(e){
plus.nativeUI.alert( "分享失败!");
});

苹果热更新问题,及微信分享问题!!!
今天通过了苹果审核。立即下载软件来测试热更新。
用wgt升级,提示 -1229 | HTML5+ Runtime缺少升级包manifest.json中配置的模块(这里显示了所有模块都缺少)。
同样的在安卓上是没有问题的。打开软件,关闭软件,升级几次后提示文件wgt文件不存在。再服务端把升级文件改名。再升级意处的升级成功了!
后来反复测试,IOS下!大部分时候升级都会提示 Runtime缺少升级包manifest.json中配置的模块(这里显示了所有模块都缺少)。重试的次数多了,就会提示wgt文件不存在,删了软件再重新安装也不行。只能服务端把升级包改名。运气好偶尔可以升级成功。
再碰上一问题,就是微信分享图片,在安卓上没问题,在IOS上分享不了。
今天通过了苹果审核。立即下载软件来测试热更新。
用wgt升级,提示 -1229 | HTML5+ Runtime缺少升级包manifest.json中配置的模块(这里显示了所有模块都缺少)。
同样的在安卓上是没有问题的。打开软件,关闭软件,升级几次后提示文件wgt文件不存在。再服务端把升级文件改名。再升级意处的升级成功了!
后来反复测试,IOS下!大部分时候升级都会提示 Runtime缺少升级包manifest.json中配置的模块(这里显示了所有模块都缺少)。重试的次数多了,就会提示wgt文件不存在,删了软件再重新安装也不行。只能服务端把升级包改名。运气好偶尔可以升级成功。
再碰上一问题,就是微信分享图片,在安卓上没问题,在IOS上分享不了。
收起阅读 »
Native.js示例汇总
官网:
http://ask.dcloud.net.cn/article/114
官网:
http://ask.dcloud.net.cn/article/114