說明 最近用到了 Paypal 支付功能,英語一般般的我也不得不硬著頭皮踩一踩這樣的坑。經過近乎半個月的作,終於實現了簡單的支付功能,那麼首先就說說使用 Paypal 必定要知道的幾點(當前日期 2018年08月07日): 1. 你應該知道 Paypal 支付功能是支持銀聯卡的,但是不支持中國買家賬 ...
說明
最近用到了 Paypal 支付功能,英語一般般的我也不得不硬著頭皮踩一踩這樣的坑。經過近乎半個月的作,終於實現了簡單的支付功能,那麼首先就說說使用 Paypal 必定要知道的幾點(當前日期 2018年08月07日):
1. 你應該知道 Paypal 支付功能是支持銀聯卡的,但是不支持中國買家賬號支付給中國賣家賬號
2. Paypal 介面有兩套,切記,產品環境和 sandbox 測試環境不同
3. 測試賬號同樣不能使用中國賬號給中國賬號付款
4. 如果你僅僅想具有固定金額的支付按鈕,用你的 Paypal 商家賬號登錄官網,配置頁裡面完全可以配置出固定的支付按鈕,然後 Copy 對應的 Html 到你的頁面就 OK 了,也就沒有必要通過更複雜的方式去支付了
5. 如果你必須動態價格和商品信息、或者你要學習基本的 Paypal 介面的話,那麼就請靜靜的往下看吧
6. 真實環境支付 Paypal 每一筆都需要收取商家賬號手續費的,並且手續費不低,如果你用真實環境測試,那麼一定要記得每一筆都申請退款吧,退款很方便,商家後臺就能直接發起,退款幾乎是實時的。
Paypal 費用說明:https://www.paypal.com/businesswallet/fees/paypal-fees
相關資料
Paypal 官方地址:https://www.paypal.com/
Paypal 官方測試地址:https://www.sandbox.paypal.com
Paypal 開發者中心:https://developer.paypal.com/
Paypal API: https://api.paypal.com
Paypal sandbox API: https://api.sandbox.paypal.com
Paypal Checkout JS 支付模式
模式圖片:
模式說明:
Checkout JS 模式是一種前端實現,使用官方提供的 Checkout.js SDK 實現支付,不需要自己寫直接調用介面的代碼,相對而言也挺簡單,但是如果你想檢測支付是否成功,你應當通過調用介面的方式驗證了。
支付部分代碼:
<div id="paypal-button"></div>
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<script type="text/javascript"> paypal.Button.render({ env: 'production', // production or sandbox 表示產品環境還是測試環境 client: { production: '', // 產品環境,值為字元串,配置實際商家號的 ClientId // sandbox: '', // 測試環境,值為字元串,配置商家測試號的 ClientId }, style: { size: 'medium', color: 'black', shape: 'pill', label: 'paypal', tagline: 'false', fundingicons: 'true' }, commit: true, payment: function (data, actions) { return actions.payment.create({ transactions: [ { amount: { total: "0.01", currency: "USD" }, description: "測試商品描述", custom: "X00002" } ], redirect_urls: { return_url: 'http://localhost:4478/Success.aspx?type=js', cancel_url: 'http://localhost:4478/Cancel.aspx' } }); }, onAuthorize: function (data, actions) { return actions.payment.execute() .then(function () { actions.redirect(); }); }, onCancel: function (data, actions) { actions.redirect(); } }, '#paypal-button'); </script>
如果你需要在支付跳轉的成功頁再次驗證一下是否支付成功,你需要自己調用官方提供的 RESTful API,參見下文的 RESTful API 支付模式
RESTful API 支付模式
說明
介面的方式很常見,和支付寶的介面類似,只是使用了 RESTful API 的模式,採用了 Basic Auth 的加密方式。使用介面的模式很常規,我們在頁面點擊按鈕調用支付介面,彈出支付頁,支付成功跳轉到成功頁面,成功頁面再調用確認支付介面確認結果。
支付介面調用:
using System; using System.Text; using System.Web.Script.Serialization; using cn.lovelong.Paypal.Config; using cn.lovelong.Paypal.Enums; using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal { /// <summary> /// CreatePayment 的摘要說明 /// </summary> public class CreatePayment { public CreatePayment() { } public PaymentResult Pay(string json) { var jsonResult = HttpHelper.PostJson( UrlConfig.CreatePaymentUrl, AccountConfig.ClientId, AccountConfig.Secret, json, Encoding.UTF8); var result = new JavaScriptSerializer().Deserialize<PaymentResult>(jsonResult); return result; } public PaymentResult Pay(PaymentParam param) { var json = GetPayParams(param); return Pay(json); } public string GetPayParams(PaymentParam param) { var total = param.Total.ToString("N"); var currency = Enum.GetName(typeof (PaypalCurrency), param.Currency); var payParams = new { intent = "sale", redirect_urls = new { return_url = param.ReturnUrl, cancel_url = param.CancelUrl, }, payer = new { payment_method = "paypal" }, transactions = new dynamic[] { new { amount = new { total = total, currency = currency }, description = param.Description, custom = param.Code, item_list = new { items = new dynamic[] { new { name = param.Name, //description = param.Name, quantity = "1", price = total, //tax = "0.01", //sku = "1", currency = currency } } } } } }; var json = new JavaScriptSerializer().Serialize(payParams); return json; } public string GetFullPayParams(decimal total, PaypalCurrency currency, string returnUrl, string cancelUrl) { var payParams = new { intent = "sale", redirect_urls = new { return_url = returnUrl, cancel_url = cancelUrl, }, payer = new { payment_method = "paypal" }, transactions = new dynamic[] { new { amount = new { total = total.ToString("N"), currency = Enum.GetName(typeof(PaypalCurrency),currency), details = new { subtotal = "30.00", tax = "0.07", shipping = "0.03", handling_fee = "1.00", shipping_discount = "-1.00", insurance = "0.01" } }, description = "", custom = "EBAY_EMS_90048630024435", invoice_number = "48787589673", payment_options = new { allowed_payment_method = "INSTANT_FUNDING_SOURCE" }, soft_descriptor = "ECHI5786786", item_list = new { items = new dynamic[] { new { name = "hat", description = "Brown hat.", quantity = "5", price = "3", tax = "0.01", sku = "1", currency = "USD" } }, shipping_address = new { recipient_name = "Brian Robinson", line1 = "4th Floor", line2 = "Unit #34", city = "San Jose", country_code = "US", postal_code = "95131", phone = "011862212345678", state = "CA" }, } } } }; var json = new JavaScriptSerializer().Serialize(payParams); return json; } } }
確認支付介面:
using System.Text; using System.Web.Script.Serialization; using cn.lovelong.Paypal.Config; using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal { /// <summary> /// Approved 的摘要說明 /// </summary> public class Approved { public PaymentResult DoJson(string paymentId, dynamic json) { var jsonResult = HttpHelper.PostJson(string.Format(UrlConfig.ApprovedUrl, paymentId), AccountConfig.ClientId, AccountConfig.Secret, json, Encoding.UTF8); var result = new JavaScriptSerializer().Deserialize<PaymentResult>(jsonResult); return result; } public PaymentResult Do(string paymentId, string payerId) { var json = GetPayParams(payerId); return DoJson(paymentId, json); } public string GetPayParams(string payerId) { var payParams = new { payer_id = payerId }; var json = new JavaScriptSerializer().Serialize(payParams); return json; } } }
查詢支付結果介面調用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Script.Serialization; using cn.lovelong.Paypal.Config; using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal { public class ShowPaymentDetails { public PaymentResult Do(string paymentId) { var json = HttpHelper.Get( string.Format(UrlConfig.ShowPaymentDetailsUrl, paymentId), AccountConfig.ClientId, AccountConfig.Secret, Encoding.UTF8); var result = new JavaScriptSerializer().Deserialize<PaymentResult>(json); return result; } } }
最容易出問題的反而是通用類 HttpHelper:
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Security.Policy; using System.Text; using System.Threading.Tasks; namespace cn.lovelong.Paypal { public class HttpHelper { public static string PostForm(string url, string userName, string password, Dictionary<string,object> dic, Encoding encoding) { var param = string.Empty; foreach (var o in dic) { if (string.IsNullOrEmpty(param)) param += o.Key + "=" + o.Value; else param += "&" + o.Key + "=" + o.Value; } byte[] byteArray = encoding.GetBytes(param); //處理HttpWebRequest訪問https有安全證書的問題( 請求被中止: 未能創建 SSL/TLS 安全通道。) ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password))); request.PreAuthenticate = true; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = byteArray.Length; //寫入參數 Stream newStream = request.GetRequestStream(); newStream.Write(byteArray, 0, byteArray.Length); newStream.Close(); using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (var stream = response.GetResponseStream()) { if(stream != null) using (StreamReader sr = new StreamReader(stream, Encoding.UTF8)) { return sr.ReadToEnd(); } } } return string.Empty; } public static string PostJson(string url, string userName, string password, string json, Encoding encoding) { byte[] byteArray = encoding.GetBytes(json); //處理HttpWebRequest訪問https有安全證書的問題( 請求被中止: 未能創建 SSL/TLS 安全通道。) ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password))); request.PreAuthenticate = true; request.Method = "POST"; request.Headers.Add("Cache-Control", "no-cache"); request.ContentType = "application/json"; request.ContentLength = byteArray.Length; //寫入參數 Stream newStream = request.GetRequestStream(); newStream.Write(byteArray, 0, byteArray.Length); newStream.Close(); using (HttpWebResponse response = (HttpWebResponse) request.GetResponse()) { using (var stream = response.GetResponseStream()) { if (stream != null) using (StreamReader sr = new StreamReader(stream, Encoding.UTF8)) { return sr.ReadToEnd(); } } } return string.Empty; } public static string Get(string url, string userName, string password, Encoding encoding) { //處理HttpWebRequest訪問https有安全證書的問題( 請求被中止: 未能創建 SSL/TLS 安全通道。) ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password))); request.PreAuthenticate = true; request.Method = "GET"; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (var stream = response.GetResponseStream()) { if (stream != null) using (StreamReader sr = new StreamReader(stream, Encoding.UTF8)) { return sr.ReadToEnd(); } } } return string.Empty; } } }
主要的功能都已經實現了!看看演示 Demo 吧!
1. 支付頁面
2. Checkout JS 方式(如果你的頁面點擊登錄之後一直在第二個頁面轉圈的話,那隻能說明你的登錄賬號不能支付你的商家賬號,或者你的賬號如果登錄之後顯示添加銀行卡的提示,說明你的商家賬號和你的賬號都是中國賬號,那你只能添加多幣種卡支付,不能用銀聯支付了):
付款就好了!
3. 介面方式我就沒有使用彈出頁面了,最簡單的方式(介面會直接在調用介面的頁面觸發支付跳轉),點擊介面支付
我就不支付了,我用的商家賬號是自己的新加坡的賬號, 按照今天的匯率 $0.01 = ¥0.068,你至少需要支付 0.07 元才能完成支付,而文章開頭也說了,商家需要付稅,也就是說你支付的 0.07 都會變成給 Paypal 的稅,商家一分錢也拿不到,也就是說,你至少支付 3.5元人民幣($0.51 = ¥3.481)商家才能得到微額的款項。
下麵給出 Demo 源碼,源碼中配置的商家號是我自己的,請自行修改,為了方便大家沒有商家賬號的朋友做測試我就不刪除了,朋友們也不要真的支付測試,你的測試只會讓 Paypal 賺錢而已!
我的開發環境是 VS2015 + C# 6.0 + JS ,代碼僅供參考,請自行修改擴展學習使用!
也可以來我自己的網站坐坐:https://blog.lovelong.cn/
![下載代碼](https://blog.lovelong.cn/wp-content/uploads/2018/01/down.jpg)