billplz可以沙箱测试
📅 2026/7/10 20:58:25
👁️ 阅读次数
📝 编程学习
请求https://www.billplz-sandbox.com/api/v3
collection_id和apikey自己去https://main.billplz-sandbox.com/或https://sso.billplz-sandbox.com/网址用自己邮箱申请沙箱帐号来获取collection_id和apikey
调用billplz账单
public void ProcessRequest(HttpContext context) { if (context.Request.Cookies["wb163CmsUserFirst"]["UserID"] == null) { context.Response.Write("{\"status\":\"2\",\"url\":\"用户未登录\"}"); return; } // 在文件顶部添加 using System.Net; ServicePointManager.Expect100Continue = true; // 启用 TLS1.2(和 TLS1.1 可选) ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | (SecurityProtocolType)768; // 仅测试环境可临时允许不验证证书(生产环境不要启用) //ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; try { decimal amount = 0m; string _baseUrl = "https://www.billplz.com/api/v3"; //string _baseUrl = "https://www.billplz-sandbox.com/api/v3"; wb163Cms.Models.WebSet webset = new wb163Cms.BLL.BLL_WebSet().loadConfig(System.Web.Hosting.HostingEnvironment.MapPath(ConfigurationManager.AppSettings["Configpath"].ToString())); amount = Convert.ToDecimal(context.Request.Params["amount"].Trim()); string paytime = context.Request.Params["paytime"].Trim(); // Billplz amount is in cents (sen) — multiply by 100 int amountSen = (int)Math.Round(amount * 100m); amountSen += 100; var payload = new { collection_id = webset.billplzcollection_id, //collection_id=webset.billplzsandcollection_id, name = wb163Cms.Common.DEncrypt.DEncrypt.Decrypt(context.Request.Cookies["wb163CmsUserFirst"]["UserID"].ToString()), email = "123@gmail.com", amount = amountSen, callback_url = webset.WebUrl+"Tools/BillplzHook.ashx",//支付成功回调函数 redirect_url = webset.WebUrl+"user.html", description = "11", reference_1 = amountSen+"" }; string url = _baseUrl + "/bills"; var req = (HttpWebRequest)WebRequest.Create(url); req.Method = "POST"; req.ContentType = "application/json"; // Basic auth: username = API key, password = empty string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(webset.billplzapikey + ":")); //string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(webset.billplzsandapikey + ":")); req.Headers["Authorization"] = "Basic " + auth; string json = JsonConvert.SerializeObject(payload); byte[] data = Encoding.UTF8.GetBytes(json); req.ContentLength = data.Length; using (var s = req.GetRequestStream()) { s.Write(data, 0, data.Length); } try { using (var resp = (HttpWebResponse)req.GetResponse()) using (var sr = new StreamReader(resp.GetResponseStream())) { string respText = sr.ReadToEnd(); Bill bill = JsonConvert.DeserializeObject<Bill>(respText); context.Response.Write("{\"status\":\"1\",\"url\":\""+bill.Url+"\"}"); return; } } catch (WebException wex) { using (var r = wex.Response as HttpWebResponse) { if (r != null) { using (var sr = new StreamReader(r.GetResponseStream())) { string error = sr.ReadToEnd(); throw new Exception("Billplz API error: " + error); } } throw; } } if (true) { context.Response.Write("1"); return; } } catch (Exception ex) { context.Response.Write("{\"status\":\"2\",\"url\":\"\"}"); return; } }回调函数BillplzHook.ashx
public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string body; using (var reader = new StreamReader(context.Request.InputStream)) { body = reader.ReadToEnd(); } wb163Cms.Models.WebSet webset = new wb163Cms.BLL.BLL_WebSet().loadConfig(System.Web.Hosting.HostingEnvironment.MapPath(ConfigurationManager.AppSettings["Configpath"].ToString())); //string xSignatureKey = webset.billplzsandXSignaturekey; string xSignatureKey = webset.billplzXSignaturekey; try { var dict = ParseQueryString(body); if (!dict.TryGetValue("x_signature", out string receivedSignature)) { context.Response.StatusCode = 500; context.Response.Write("error"); } bool isValid = VerifySignature(dict, xSignatureKey, receivedSignature); string paid = dict.TryGetValue("paid", out var paid1) ? paid1 : "";//支付成功状态 string amount = dict.TryGetValue("amount", out var amount1) ? amount1 : "";//金额 string userid = dict.TryGetValue("name", out var userid1) ? userid1 : "";//用户id decimal money = Convert.ToDecimal(amount) / 100M;//马币单位是仙要除去100才能是1马币 money -= 1M; BLL_User ubll = new BLL_User(); wb163Cms.Models.User modeluser = ubll.GetModel(int.Parse(userid)); if (paid == "true"&&isValid)//支付成功 { //充值直接审核 wb163Cms.BLL.BLL_MoneyPay bll = new BLL.BLL_MoneyPay(); wb163Cms.Models.MoneyPay model = new Models.MoneyPay(); model.UserName = modeluser.UserName; model.UserID = modeluser.Id; model.Pic = ""; model.User_note = ""; model.RefNo = ""; model.Payway = 0; model.BankName = "在线支付"; model.Mongeytype = 0; model.Entermoney = money; model.Paytime = DateTime.Now; model.Status = 1; model.Addtime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); model.Checktime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); model.Oppter = ""; model.Oppter_note = ""; model = bll.Add(model); //wb163Cms.BLL.BLL_User blluser = new BLL.BLL_User(); //充值记录 wb163Cms.BLL.BLL_User_charge_record bllrecord = new BLL.BLL_User_charge_record(); wb163Cms.Models.User_charge_record modelrecord = new Models.User_charge_record(); modelrecord.Amount = money; modelrecord.Balance = modeluser.Point; modelrecord.Currency = "RM"; modelrecord.Afterpay = modelrecord.Balance + modelrecord.Amount; modelrecord.Posttime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); modelrecord.Type = 1; modelrecord.Remark = "提交充值审核通过"; modelrecord.User_id = model.UserID; bllrecord.Add(modelrecord); //更新余额 modeluser.Point += model.Entermoney; ubll.Update(modeluser); //ubll.UpdateField(userid, " point=point+" + money + " ");//更新余额 //context.Response.Redirect("/user.html"); context.Response.StatusCode = 200; context.Response.Write("ok"); } else { wb163Cms.Common.FsLog.CreateLog("billplz 更新报错", true); context.Response.StatusCode = 500; context.Response.Write("error"); } } catch (Exception ex) { // 记录日志 wb163Cms.Common.FsLog.CreateLog("billplz webhook error:" + ex.Message, true); context.Response.StatusCode = 500; context.Response.Write("error"); } } //解析&分隔的数据为Dictionary static Dictionary<string, string> ParseQueryString(string qs) { var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrEmpty(qs)) return result; // 如果传入的是完整 URL,截取 ? 之后的查询部分 int q = qs.IndexOf('?'); if (q >= 0) qs = qs.Substring(q + 1); var parts = qs.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts) { int eq = part.IndexOf('='); string key, value; if (eq >= 0) { key = part.Substring(0, eq); value = part.Substring(eq + 1); } else { key = part; value = string.Empty; } // 使用 WebUtility.UrlDecode 做解码(处理 %xx 和 +) key = WebUtility.UrlDecode(key ?? string.Empty); value = WebUtility.UrlDecode(value ?? string.Empty); result[key] = value; } return result; } //验签 public static bool VerifySignature(Dictionary<string, string> parameters, string xSignatureKey, string receivedSignature) { if (parameters == null || string.IsNullOrEmpty(xSignatureKey) || string.IsNullOrEmpty(receivedSignature)) return false; // 1. 按照键名 ASCII 升序排列,并拼接成 key1value1key2value2 格式 var sortedParams = parameters .Where(p => !string.IsNullOrEmpty(p.Key) && p.Key != "x_signature") // 确保排除 x_signature .OrderBy(p => p.Key+p.Value, StringComparer.Ordinal); var sourceString = new StringBuilder(); foreach (var param in sortedParams) { sourceString.Append(param.Key).Append(param.Value+"|"); } sourceString.Remove(sourceString.Length - 1, 1); Common.FsLog.CreateLog("签名"+sourceString.ToString(), true); // 2. 使用 HMAC-SHA256 计算签名 var encoding = new UTF8Encoding(); byte[] keyBytes = encoding.GetBytes(xSignatureKey); byte[] messageBytes = encoding.GetBytes(sourceString.ToString()); using (var hmac = new HMACSHA256(keyBytes)) { byte[] hashBytes = hmac.ComputeHash(messageBytes); string calculatedSignature = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); // 3. 比对计算出的签名与接收到的签名 return string.Equals(calculatedSignature, receivedSignature, StringComparison.OrdinalIgnoreCase); } }
编程学习
技术分享
实战经验