簡體   English   中英

PayPal IPN是否有任何樣本

[英]Is there any sample for PayPal IPN

我有一個Asp.Net WEB API 2項目,我想實現一個即時支付通知(IPN)監聽器控制器。

我找不到任何示例和nuget包。 我只需要承認用戶使用Paypal上的標准html按鈕付款。 這很簡單。

所有nuget包都是創建發票或自定義按鈕。 這不是我需要的

paypal上的示例適用於經典的asp.net,而不適用於MVC或WEB API MVC

我確定有人已經這樣做了,當我開始編碼時,我感覺我正在重新發明輪子。

有沒有IPN監聽器控制器示例?

至少一個PaypalIPNBindingModel綁定Paypal查詢。

    [Route("IPN")]
    [HttpPost]
    public IHttpActionResult IPN(PaypalIPNBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }

        return Ok();
    }

編輯

到目前為止,我有以下代碼

        [Route("IPN")]
        [HttpPost]
        public void IPN(PaypalIPNBindingModel model)
        {
            if (!ModelState.IsValid)
            {
                // if you want to use the PayPal sandbox change this from false to true
                string response = GetPayPalResponse(model, true);

                if (response == "VERIFIED")
                {

                }
            }
        }

        string GetPayPalResponse(PaypalIPNBindingModel model, bool useSandbox)
        {
            string responseState = "INVALID";

            // Parse the variables
            // Choose whether to use sandbox or live environment
            string paypalUrl = useSandbox ? "https://www.sandbox.paypal.com/"
            : "https://www.paypal.com/";

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(paypalUrl);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));

                //STEP 2 in the paypal protocol
                //Send HTTP CODE 200
                HttpResponseMessage response = client.PostAsJsonAsync("cgi-bin/webscr", "").Result;

                if (response.IsSuccessStatusCode)
                {
                    //STEP 3
                    //Send the paypal request back with _notify-validate
                    model.cmd = "_notify-validate";
                    response = client.PostAsync("cgi-bin/webscr", THE RAW PAYPAL REQUEST in THE SAME ORDER ).Result;

                    if(response.IsSuccessStatusCode)
                    {
                        responseState = response.Content.ReadAsStringAsync().Result;
                    }
                }
            }

            return responseState;
        }

但對於第3步,我試圖將我的模型發布為json,但paypal返回HTML頁面而不是VALIDATED或INVALID。 我發現我必須使用application/x-www-form-urlencoded ,並且參數的順序相同。

如何獲取請求URL?

我會使用查詢Url並&cmd=_notify-validate添加&cmd=_notify-validate

基於已接受的答案,我提出了以下代碼實現ASP.NET MVC的IPN偵聽器。 該解決方案已經部署並且似乎正常運行。

[HttpPost]
public async Task<ActionResult> Ipn()
{
    var ipn = Request.Form.AllKeys.ToDictionary(k => k, k => Request[k]);
    ipn.Add("cmd", "_notify-validate");

    var isIpnValid = await ValidateIpnAsync(ipn);
    if (isIpnValid)
    {
        // process the IPN
    }

    return new EmptyResult();
}

private static async Task<bool> ValidateIpnAsync(IEnumerable<KeyValuePair<string, string>> ipn)
{
    using (var client = new HttpClient())
    {
        const string PayPalUrl = "https://www.paypal.com/cgi-bin/webscr";

        // This is necessary in order for PayPal to not resend the IPN.
        await client.PostAsync(PayPalUrl, new StringContent(string.Empty));

        var response = await client.PostAsync(PayPalUrl, new FormUrlEncodedContent(ipn));

        var responseString = await response.Content.ReadAsStringAsync();
        return (responseString == "VERIFIED");
    }
}

編輯:

讓我分享一下我的經驗 - 上面的代碼到目前為止工作得很好,但突然之間它正在處理一個IPN失敗,即responseString == "INVALID"

問題原來是我的帳戶設置為使用charset == windows-1252 ,這是PayPal的默認值。 但是, FormUrlEncodedContent使用UTF-8進行編碼,因此驗證失敗,因為國家字符如“ř”。 解決方案是將charset設置為UTF-8,可以在Profile>我的銷售工具> PayPal按鈕語言編碼>更多選項中完成,請參閱此SO線程

這是我的代碼

隨意回顧是有問題的

        [Route("IPN")]
        [HttpPost]
        public IHttpActionResult IPN()
        {
            // if you want to use the PayPal sandbox change this from false to true
            string response = GetPayPalResponse(true);

            if (response == "VERIFIED")
            {
                //Database stuff
            }
            else
            {
                return BadRequest();
            }

            return Ok();
        }

        string GetPayPalResponse(bool useSandbox)
        {
            string responseState = "INVALID";
            // Parse the variables
            // Choose whether to use sandbox or live environment
            string paypalUrl = useSandbox ? "https://www.sandbox.paypal.com/"
            : "https://www.paypal.com/";

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(paypalUrl);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));

                //STEP 2 in the paypal protocol
                //Send HTTP CODE 200
                HttpResponseMessage response = client.PostAsJsonAsync("cgi-bin/webscr", "").Result;

                if (response.IsSuccessStatusCode)
                {
                    //STEP 3
                    //Send the paypal request back with _notify-validate
                    string rawRequest = response.Content.ReadAsStringAsync().Result;
                    rawRequest += "&cmd=_notify-validate";

                    HttpContent content = new StringContent(rawRequest);

                    response = client.PostAsync("cgi-bin/webscr", content).Result;

                    if(response.IsSuccessStatusCode)
                    {
                        responseState = response.Content.ReadAsStringAsync().Result;
                    }
                }
            }

            return responseState;
        }

擴展Michal Hosala的答案 ,與PayPal成功握手需要兩件事

首先,在向PayPal發出請求之前設置安全協議

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

其次,避免使用字典,因為要進行驗證,PayPal要求數據以相同的順序發回,並以cmd變量開頭。 我最終這樣做了

Request.InputStream.Seek(0, SeekOrigin.Begin);
string rawRequestBody = new StreamReader(Request.InputStream).ReadToEnd();
var ipnVarsWithCmd = rawRequestBody.Split('&').Select(x => new KeyValuePair<string, string>(x.Split('=')[0], x.Split('=')[1])).ToList();
ipnVarsWithCmd.Insert(0, new KeyValuePair<string, string>("cmd", "_notify-validate"));

我也在尋找類似OP的原始問題的解決方案Is there any IPN listener controller example? At least a PaypalIPNBindingModel to bind the Paypal query. Is there any IPN listener controller example? At least a PaypalIPNBindingModel to bind the Paypal query. 我到了這個頁面。 我嘗試了這個線程中提到的其他解決方案,它們都工作但我真的需要PayPal查詢到模型解決方案所以我谷歌搜索直到我偶然發現Carlos Rodriguez的創建PayPal IPN Web API Endpoint博客帖子。

以下是卡洛斯所做的概述:

  1. 創建一個模型。 根據您從PayPal獲得的ipn響應,在模型中定義屬性。

     public class IPNBindingModel { public string PaymentStatus { get; set; } public string RawRequest { get; set; } public string CustomField { get; set; } } 
  2. 創建一個PayPal Validator類。

     public class PayPalValidator { public bool ValidateIPN(string body) { var paypalResponse = GetPayPalResponse(true, body); return paypalResponse.Equals("VERIFIED"); } private string GetPayPalResponse(bool useSandbox, string rawRequest) { string responseState = "INVALID"; string paypalUrl = useSandbox ? "https://www.sandbox.paypal.com/" : "https://www.paypal.com/"; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; using (var client = new HttpClient()) { client.BaseAddress = new Uri(paypalUrl); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); HttpResponseMessage response = client.PostAsJsonAsync("cgi-bin/webscr", "").Result; if (response.IsSuccessStatusCode) { rawRequest += "&cmd=_notify-validate"; HttpContent content = new StringContent(rawRequest); response = client.PostAsync("cgi-bin/webscr", content).Result; if (response.IsSuccessStatusCode) { responseState = response.Content.ReadAsStringAsync().Result; } } } return responseState; } } 
  3. 創建您的控制器。

     [RoutePrefix("paypal")] public class PayPalController : ApiController { private PayPalValidator _validator; public PayPalController() { this._validator = new PayPalValidator(); } [HttpPost] [Route("ipn")] public void ReceiveIPN(IPNBindingModel model) { if (!_validator.ValidateIPN(model.RawRequest)) throw new Exception("Error validating payment"); switch (model.PaymentStatus) { case "Completed": //Business Logic break; } } } 
  4. 創建一個模型綁定器,用於定義Web Api如何自動為您創建模型。

     public class IPNModelBinder : IModelBinder { public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(IPNBindingModel)) { return false; } var postedRaw = actionContext.Request.Content.ReadAsStringAsync().Result; Dictionary postedData = ParsePaypalIPN(postedRaw); IPNBindingModel ipn = new IPNBindingModel { PaymentStatus = postedData["payment_status"], RawRequest = postedRaw, CustomField = postedData["custom"] }; bindingContext.Model = ipn; return true; } private Dictionary ParsePaypalIPN(string postedRaw) { var result = new Dictionary(); var keyValuePairs = postedRaw.Split('&'); foreach (var kvp in keyValuePairs) { var keyvalue = kvp.Split('='); var key = keyvalue[0]; var value = keyvalue[1]; result.Add(key, value); } return result; } } } 
  5. 將模型綁定器注冊到WebApiConfig.cs。 config.BindParameter(typeof(IPNBindingModel), new IPNModelBinder());

希望這有助於其他人。 感謝Carlos Rodriguez提供的令人驚嘆的代碼。

這里有一個官方的c#示例: https//github.com/paypal/ipn-code-samples in path \\c#\\paypal_ipn_mvc.cs

C#示例顯示了一個ASP.NET MVC控制器,其中包含響應IPN的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM