目录
Xamarin.Forms 微信支付宝支付接口
背景
接口实现
Xamarin.Forms 微信支付宝支付接口
背景
服务端已写好的,这里的是手机端的调用。
接口实现
微信接口
1. 在xamarin.forms工程中添加接口
public interface IWeChat
{
void WXPay(Xamarin.AppFramework.WxPayReqItem req);
}
1.1 微信支付请求参数的构成:以下代码using内容一样
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
/// <summary>
/// 微信客户端付款请求参数:从服务器返回的。
/// 字段名称根据服务端定义的参数自行修改
/// </summary>
public class WxPayReqItem
{
public string prepay_id { get; set; }
public string nonceStr { get; set; }
public string sign
{
get
{
return WxInfo.SecSign(prepay_id, nonceStr,timestamp);
}
}
public string timestamp
{
get
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
}
public string timestamp
{
get
{
TimeSpan ts = operatedate - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
}
/// <summary>
/// 时间戳
/// </summary>
[JsonIgnore]
public DateTime operatedate { get; set; }
}
/// <summary>
/// 微信信息
/// </summary>
public class WxInfo
{
/// <summary>
/// 微信appid
/// </summary>
public static string AppId = "";
/// <summary>
/// 商户号
/// </summary>
public static string PartnerId = " ";
/// <summary>
/// /商户平台API密钥
/// </summary>
public static string outkey = " ";
/// <summary>
/// 支付结果回调 ,回调结果里面带上支付方式和支付标志。
/// </summary>
public static AsyncMsg am_支付结果 = new AsyncMsg();
public static WxUserInfo wxUserInfo = new WxUserInfo();
/// <summary>
/// 微信支付签名算法
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
static string GetSignContent(IDictionary<string, string> parameters)
{
// 第一步:把字典按Key的字母顺序排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder("");
while (dem.MoveNext())
{
string key = dem.Current.Key;
string value = dem.Current.Value;
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
query.Append(key).Append("=").Append(value).Append("&");
}
}
string content = query.ToString().Substring(0, query.Length - 1);
return content;
}
/// <summary>
/// 微信支付二次签名
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string SecSign(string prepayid, string noncestr,string timestamp)
{
//二次签名
Dictionary<string, string> parameters1 = new Dictionary<string, string>();
parameters1.Add("appid", AppId);
parameters1.Add("partnerid", PartnerId);
parameters1.Add("prepayid", prepayid);
parameters1.Add("noncestr", noncestr);
parameters1.Add("timestamp", timestamp);
parameters1.Add("package", "Sign=WXPay");
string content = GetSignContent(parameters1);
//,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
//stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户平台设置的密钥key
string signResult =Xamarin.AppFramework.Helpers.CryptHelper.GetMD5(content + "&key=" + outkey).ToUpper();
return signResult;
}
}
/// <summary>
/// 微信用户信息
/// </summary>
/// <returns></returns>
public class WxUserInfo
{
/// <summary>
/// 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
/// </summary>
public string subscribe { get; set; }
/// <summary>
/// 用户的标识,对当前公众号唯一
/// </summary>
public string openid { get; set; }
/// <summary>
/// 用户的昵称
/// </summary>
public string nickname { get; set; }
/// <summary>
/// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
/// </summary>
public string sex { get; set; }
/// <summary>
/// 用户所在城市
/// </summary>
public string city { get; set; }
/// <summary>
/// 用户所在国家
/// </summary>
public string country { get; set; }
/// <summary>
/// 用户所在省份
/// </summary>
public string province { get; set; }
/// <summary>
/// 用户的语言,简体中文为zh_CN
/// </summary>
public string language { get; set; }
/// <summary>
/// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
/// 例子:http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0
/// </summary>
public string headimgurl { get; set; }
/// <summary>
/// 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
/// </summary>
public string subscribe_time { get; set; }
/// <summary>
/// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
/// </summary>
public string unionid { get; set; }
/// <summary>
/// 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
/// </summary>
public string remark { get; set; }
/// <summary>
/// 用户所在的分组ID(兼容旧的用户分组接口)
/// </summary>
public string groupid { get; set; }
/// <summary>
/// 用户被打上的标签ID列表
/// </summary>
public string tagid_list { get; set; }
/// <summary>
/// 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,
/// ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,
/// ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,
/// ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他
/// </summary>
public string subscribe_scene { get; set; }
/// <summary>
/// 二维码扫码场景(开发者自定义)
/// </summary>
public string qr_scene { get; set; }
/// <summary>
/// 二维码扫码场景描述(开发者自定义)
/// </summary>
public string qr_scene_str { get; set; }
}
上面用到了MD5加密:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
namespace Xamarin.AppFramework.Helpers
{
public class CryptHelper
{
private static readonly MD5 md5Hasher = MD5.Create();
public static string CreateMD5(string text)
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(text));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
public static string GetMD5(string encypStr)
{
char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
try
{
byte[] btInput = System.Text.Encoding.Default.GetBytes(encypStr);
// 获得MD5摘要算法的 MessageDigest 对象
MD5 mdInst = System.Security.Cryptography.MD5.Create();
// 使用指定的字节更新摘要
mdInst.ComputeHash(btInput);
// 获得密文
byte[] md = mdInst.Hash;
// 把密文转换成十六进制的字符串形式
int j = md.Length;
char[] str = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++)
{
byte byte0 = md[i];
str[k++] = hexDigits[(int)(((byte)byte0) >> 4) & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new string(str);
}
catch (Exception e)
{
Console.Error.WriteLine(e.StackTrace);
return null;
}
}
}
}
2. android平台下实现:用到了plugin.currentActivity,需要nuget下载引用。 2.1 2.1 实现接口
using Android.Content;
using Android.Graphics;
using Android.Widget;
using AndroidHUD;
using Com.Tencent.MM.Sdk.Modelmsg;
using Com.Tencent.MM.Sdk.Openapi;
using System;
using System.IO;
using Saobag.Droid;
[assembly: Xamarin.Forms.Dependency(typeof(Interface.Droid.WeChat))]
namespace Interface.Droid
{
/// <summary>
/// 微信相关
/// </summary>
class WeChat : Xamarin.AppFramework.Interface.IWeChat
{
Context CurrentContext => Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity;
/// <summary>
/// 微信支付
/// </summary>
public void WXPay(Xamarin.AppFramework.WxPayReqItem wxreq)
{
try
{
wxapi.Droid.WXPayEntryActivity.wxpay(wxreq);
}
catch (Exception ex)
{
Android.Widget.Toast.MakeText(CurrentContext, "支付异常:" + ex.Message, ToastLength.Long).Show();
}
}
}
}
2.2 微信支付及回调接口
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget;
using Com.Tencent.MM.Sdk.Modelbase;
using Com.Tencent.MM.Sdk.Openapi;
using Com.Tencent.MM.Sdk.Modelpay;
using Xamarin.Forms;
namespace wxapi.Droid
{
//xamarin对于Activity的路径是根据Name来发起的, 本身命名空间和类名只是工程内部使用。
//微信回调的根据是这里的Name: 应用包名+.wxapi.WXPayEntryActivity 构成,不能有差错,否则无法回调。
[Activity(Label = "WXPayEntryActivity", Name = "com.cstc.Saobag.wxapi.WXPayEntryActivity", Theme = "@android:style/Theme.Translucent", LaunchMode = Android.Content.PM.LaunchMode.SingleTop, Exported = true)]
///微信支付回调
public class WXPayEntryActivity : Activity, IWXAPIEventHandler
{
private static IWXAPI wxApi;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
wxApi = WXAPIFactory.CreateWXAPI(this, Xamarin.AppFramework.WxInfo.AppId);
wxApi.HandleIntent(Intent, this);
}
public void OnReq(BaseReq req)
{
// throw new NotImplementedException();
}
public void OnResp(BaseResp resp)
{
switch (resp.MyErrCode)
{
case BaseResp.ErrCode.ErrOk:
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this.BaseContext, "支付成功", ToastLength.Short).Show();
});
Xamarin.AppFramework.WxInfo.am_支付结果.OnCompletion(null, "充值");
Finish();
break;
case BaseResp.ErrCode.ErrAuthDenied:
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this.BaseContext, "支付失败", ToastLength.Short).Show();
});
Xamarin.AppFramework.WxInfo.am_支付结果.OnCancel(null, "充值");
Finish();
break;
case BaseResp.ErrCode.ErrUserCancel:
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this.BaseContext, "取消支付", ToastLength.Short).Show();
});
Xamarin.AppFramework.WxInfo.am_支付结果.OnCancel(null, "充值");
Finish();
break;
}
}
/// <summary>
/// 调用微信支付
/// </summary>
/// <param name="req"></param>
public static void wxpay(Xamarin.AppFramework.WxPayReqItem req)
{
if (wxApi == null)
{
//不同工程使用的时候记得修改命名空间。
wxApi = WXAPIFactory.CreateWXAPI(Saobag.Droid.MainActivity.mainActivity, Xamarin.AppFramework.WxInfo.AppId);
}
wxApi.RegisterApp(Xamarin.AppFramework.WxInfo.AppId);
PayReq payReq = new PayReq();
payReq.AppId = Xamarin.AppFramework.WxInfo.AppId;
payReq.PartnerId = Xamarin.AppFramework.WxInfo.PartnerId;
payReq.PrepayId = req.prepay_id;
payReq.PackageValue = "Sign=WXPay";
payReq.NonceStr = req.nonceStr;
payReq.TimeStamp = req.timestamp;
payReq.Sign = req.sign;
bool isflag = wxApi.SendReq(payReq); }
}
}
2.3 支付宝android直接实现接口,处理回调值。
using Android.Content;
using Android.Widget;
using System;
using Xamarin.Forms;
using Saobag.Droid;
[assembly: Xamarin.Forms.Dependency(typeof(Interface.Droid.Ali))]
namespace Interface.Droid
{
public class Ali : Xamarin.AppFramework.Interface.IAli
{
/// <summary>
/// Android 实现支付宝支付
/// </summary>
/// <param name="orderInfo">订单信息</param>
public void AliPay(string orderInfo)
{
Com.Alipay.Sdk.App.PayTask pa = new Com.Alipay.Sdk.App.PayTask(MainActivity.mainActivity);
string result = pa.Pay(orderInfo);
if (result.Contains("6001"))
{
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(MainActivity.mainActivity, "取消支付", ToastLength.Short).Show();
});
Xamarin.AppFramework.AliInfo.am_支付结果.OnCancel(null, "充值");
}
else if (result.Contains("9000"))
{
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(MainActivity.mainActivity, "支付成功", ToastLength.Short).Show();
});
Xamarin.AppFramework.AliInfo.am_支付结果.OnCompletion(null, "充值");
}
else if (result.Contains("4000"))
{
Device.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(MainActivity.mainActivity, "支付失败", ToastLength.Short).Show();
});
Xamarin.AppFramework.AliInfo.am_支付结果.OnCancel(null, "充值");
}
}
}
}
3. IOS平台下实现:
首先要配置:infolist,高级里面要添加url类型,用于支付后跳回程序。微信为appid,支付宝为scheme,也就是向支付宝申请支付填的包名
3.1 实现接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Foundation;
using UIKit;
using Xamarin.Forms;
using WeChatBinding;
[assembly: Xamarin.Forms.Dependency(typeof(Interface.iOS.WeChat))]
namespace Interface.iOS
{
public class WeChat : Xamarin.AppFramework.Interface.IWeChat
{
public static WXApi WXapi;
public void WXPay(Xamarin.AppFramework.WxPayReqItem req)
{
PayReq request = new PayReq();
request.PartnerId = Xamarin.AppFramework.WxInfo.PartnerId;
request.PrepayId = req.prepay_id;
request.Package = @"Sign=WXPay";
request.NonceStr = req.nonceStr;
request.TimeStamp = Convert.ToUInt32(req.timestamp);
request.Sign = req.sign;
WXApi.SendReq(request);
}
}
}
using AlipaySDKBinding;
using Xamarin.Forms;
[assembly: Xamarin.Forms.Dependency(typeof(Interface.iOS.Ali))]
namespace Interface.iOS
{
public class Ali : Xamarin.AppFramework.Interface.IAli
{
/// <summary>
/// 支付报支付
/// </summary>
/// <param name="orderInfo">订单明细</param>
public void AliPay(string orderInfo)
{
//支付宝回调实际上不走这里的CompletionBlock,而是走appdelegate里面的openurl
//注意scheme需要在infolist--高级,urltype那里添加软件的标识符,这里调用传入,支付成功后才能返回软件
AlipaySDKBinding.AlipaySDK.DefaultService.PayOrder(orderInfo, "com.cstc.sample",
new CompletionBlock(aa => { }));
}
}
}
3.2 微信回调委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Foundation;
using UIKit;
using WeChatBinding;
using BigTed;
using Xamarin.Forms;
using System.Threading.Tasks;
namespace wxapi
{
public class WxDelegate : WXApiDelegate
{
public override void OnReq(BaseReq req)
{
//base.OnReq(req);
}
public override void OnResp(BaseResp resp)
{
if (resp is PayResp)
{
#region 微信支付回调
PayResp response = (PayResp)resp;
switch (response.ErrCode)
{
case 0:
Show_Toast("支付成功");
Xamarin.AppFramework.WxInfo.am_支付结果.OnCompletion(null, "充值");
break;
default:
Xamarin.AppFramework.WxInfo.am_支付结果.OnCancel(null, "充值");
Show_Toast("支付失败");
break;
}
#endregion
}
}
}
}
}
3.3 支付回调
using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
using WeChatBinding;
using BigTed;
namespace Saobag.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Xamarin.AppFramework.MConfig.screenWidth = UIScreen.MainScreen.Bounds.Width; //屏幕宽度
WXApi.RegisterApp(Xamarin.AppFramework.WxInfo.AppId);
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
/// <summary>
/// 支付宝支付回调, IOS9.3以上
/// </summary>
/// <param name="app"></param>
/// <param name="url"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
return ThirpartyResult(url);
}
/// <summary>
/// 微信回调,IOS9.3以下支付宝支付回调
/// </summary>
/// <param name="application"></param>
/// <param name="url"></param>
/// <param name="sourceApplication"></param>
/// <param name="annotation"></param>
/// <returns></returns>
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
return ThirpartyResult(url);
}
private bool ThirpartyResult(NSUrl url)
{
Xamarin.AppFramework.MConfig.WriteLog(url.ToString());
if (url.Scheme.Equals(Xamarin.AppFramework.WxInfo.AppId))
{
wxapi.WxDelegate d = new wxapi.WxDelegate();
bool b = WXApi.HandleOpenURL(url, d);
return b;
}
else if (url.Host.Equals("safepay"))
{
return AliPayResult(url);
}
else
return true;
}
private bool AliPayResult(NSUrl url)
{
if (url.Host.Equals("safepay"))
{
string resultStatus = "";
try
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
AlipaySDKBinding.AlipaySDK.DefaultService.ProcessOrderWithPaymentResult(url,
new AlipaySDKBinding.CompletionBlock(aa =>
{
try
{
string result = System.Web.HttpUtility.UrlDecode(url.ToString());
string[] lists = result.Split("?"); //分解获取?后面的内容
Xamarin.AppFramework.AliResponse user = (Xamarin.AppFramework.AliResponse)Newtonsoft.Json.JsonConvert.DeserializeObject(lists[1], typeof(Xamarin.AppFramework.AliResponse));
resultStatus = user.memo.ResultStatus;
}
catch (Exception ex)
{
BTProgressHUD.ShowToast("支付失败", true, 2000);
Xamarin.AppFramework.AliInfo.am_支付结果.OnCancel(null, "支付宝支付");
}
}));
如果支付失败
if (!resultStatus.Equals("9000"))
{
BTProgressHUD.ShowToast("支付失败", true, 2000);
Xamarin.AppFramework.AliInfo.am_支付结果.OnCancel(null, "支付宝支付");
}
else
{
BTProgressHUD.ShowToast("支付成功", true, 2000);
//如果支付成功
Xamarin.AppFramework.AliInfo.am_支付结果.OnCompletion(null, "支付宝支付");
}
});
}
catch (Exception ex)
{
return false;
}
}
return true;
}
}
}
忘记把这个回调类补上。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Xamarin.AppFramework
{
/// <summary>
/// 异步回调
/// </summary>
public class AsyncMsg
{
/// <summary>
/// 委托事件
/// </summary>
/// <param name="tag">数据内容</param>
/// <param name="e">消息</param>
public delegate void AsyncMsgEventHandler(object tag, string e);
/// <summary>
/// 完成
/// </summary>
public event AsyncMsgEventHandler Completion;
/// <summary>
/// 取消或失败
/// </summary>
public event AsyncMsgEventHandler Cancel;
public bool IsCompletion = false;//是否完成
public bool IsCancel = false;//是否取消
/// <summary>
///
/// </summary>
public string Tag { get; set; } = "";
public object TagObj { get; set; } = "";
/// <summary>
/// 完成时触发
/// </summary>
/// <param name="tag">数据内容</param>
/// <param name="e">消息</param>
public void OnCompletion(object tag, string e)
{
if (Completion != null)
{
Completion(tag, e);
IsCompletion = true;
}
}
public void OnCompletion()
{
OnCompletion(null, "");
}
/// <summary>
/// 取消或失败触发
/// </summary>
/// <param name="tag">数据内容</param>
/// <param name="e">消息</param>
public void OnCancel(object tag, string e)
{
if (Cancel != null)
{
Cancel(tag, e);
IsCancel = true;
}
}
public void OnCancel()
{
OnCancel(null, "");
}
}
}
2020-1-2 补充说明,之前的时间戳用下面这个写法有问题, return datime.UtcNow,每一次使用timestamp的时候,值都会发生变化,所以 sign加密时候的时间戳的值和上传的时间戳其实是不一致的,只是因为取得是秒值,所以发生错误的概率不大。今天调用了另一个业务接口,是毫秒级的,一直签名错误,发现了这个问题,所以我在上面加了一个字段operatedate,在调用的时候赋值,时间戳就不会发生变化。
public string timestamp
{
get
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
}