Paypal 支付功能的 C# .NET / JS 實現

来源:https://www.cnblogs.com/herbertchina/archive/2018/08/08/9441790.html
-Advertisement-
Play Games

說明 最近用到了 Paypal 支付功能,英語一般般的我也不得不硬著頭皮踩一踩這樣的坑。經過近乎半個月的作,終於實現了簡單的支付功能,那麼首先就說說使用 Paypal 必定要知道的幾點(當前日期 2018年08月07日): 1. 你應該知道 Paypal 支付功能是支持銀聯卡的,但是不支持中國買家賬 ...


說明

最近用到了 Paypal 支付功能,英語一般般的我也不得不硬著頭皮踩一踩這樣的坑。經過近乎半個月的作,終於實現了簡單的支付功能,那麼首先就說說使用 Paypal 必定要知道的幾點(當前日期 2018年08月07日):

1. 你應該知道 Paypal 支付功能是支持銀聯卡的,但是不支持中國買家賬號支付給中國賣家賬號

2. Paypal 介面有兩套,切記,產品環境和 sandbox 測試環境不同

3. 測試賬號同樣不能使用中國賬號給中國賬號付款

4. 如果你僅僅想具有固定金額的支付按鈕,用你的 Paypal 商家賬號登錄官網,配置頁裡面完全可以配置出固定的支付按鈕,然後 Copy 對應的 Html 到你的頁面就 OK 了,也就沒有必要通過更複雜的方式去支付了

image

5. 如果你必須動態價格和商品信息、或者你要學習基本的 Paypal 介面的話,那麼就請靜靜的往下看吧

6. 真實環境支付 Paypal 每一筆都需要收取商家賬號手續費的,並且手續費不低,如果你用真實環境測試,那麼一定要記得每一筆都申請退款吧,退款很方便,商家後臺就能直接發起,退款幾乎是實時的。

Paypal 費用說明:https://www.paypal.com/businesswallet/fees/paypal-fees

image

image

 

相關資料

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 支付模式

模式圖片:

 image

模式說明:

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. 支付頁面
image 
2. Checkout JS 方式(如果你的頁面點擊登錄之後一直在第二個頁面轉圈的話,那隻能說明你的登錄賬號不能支付你的商家賬號,或者你的賬號如果登錄之後顯示添加銀行卡的提示,說明你的商家賬號和你的賬號都是中國賬號,那你只能添加多幣種卡支付,不能用銀聯支付了):
image 
image 
image 
付款就好了!
3. 介面方式我就沒有使用彈出頁面了,最簡單的方式(介面會直接在調用介面的頁面觸發支付跳轉),點擊介面支付
image 
image 
image 
image 
我就不支付了,我用的商家賬號是自己的新加坡的賬號, 按照今天的匯率 $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/
 
下載代碼
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前置說明 安裝 Service Fabric SDK,會在本機 C:\Program Files\Microsoft SDKs\Service Fabric\Tools\PSModule\ServiceFabricSDK 生成部署腳本,如下圖: 用VS創建一個 Service Fabric 應用後, ...
  • 從上面的介紹可以看出,SignalR既然是為實時而生的,這樣就決定了其使用場所。具體適用情景有如下幾點: 聊天室,如線上客服系統,IM系統等 股票價格實時更新 消息的推送服務 游戲中人物位置的實時推送 : 游戲參考 https://blog.csdn.net/jaswhen/article/deta ...
  • MemCahe 首先介紹下memcahce的定義:是一個分散式的高速緩存系統,目前被許多網站使用以提升網站的訪問速度,尤其對於一些大型的、需要頻繁訪問資料庫的網站訪問速度提升效果十分顯著。 接下來介紹下在windows下的memcache的安裝與使用: 第一步:下載memcache安裝包 鏈接:下載 ...
  • 簡介 本篇文章將介紹C# 如何處理Excel圖形相關的問題,包括以下內容要點: 1.繪製圖形 1.1 繪製圖形並添加文本到圖形 1.2 添加圖片到圖形 1.3 設置圖形陰影效果 2. 提取圖形中的文本、圖片 3. 設置圖形的顯示、隱藏 4. 刪除圖形 4.1刪除指定圖形 4.2 刪除所有圖形 所需工 ...
  • 上一篇博文中已經實現瞭如何在頁面上使用自定義的屬性即上篇博文所示的@this.U,今天將進一步研究用戶自定義User Identity; 實現思路: 通過研究微軟自帶identity的套路,我們可以發現其實現此功能的介面為IIdentity、System.Security.Principal.IPr ...
  • 什麼是Polly? Polly是一個.NET彈性和瞬態故障處理庫.允許我們以非常順暢和線程安全的方式來執行諸如行重試,斷路,超時,故障恢復等策略。 Polly項目地址:https://github.com/App-vNext/Polly Polly提供多種彈性策略:重試(Retry),斷路器(Cir ...
  • ASP.NET MVC 是微軟官方提供的以MVC模式為基礎的ASP.NET Web應用程式(Web Application)框架,它由Castle的MonoRail而來。 MVC 編程模式 MVC 是三種 ASP.NET 編程模式中的一種。 MVC 是一種使用 MVC(Model View Cont ...
  • 在開發中有時會用到Request.Params["id"]來獲取參數,那麼到底是從什麼地方接收參數呢? 一般情況下,有三種方式進行參數傳遞1、GET 方式,通過url傳遞,如?id=2、POST方式,通過Form傳遞3、Cookie方式 此處要用[]!!! 對於GET方式傳遞的參數,我們使用Requ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...