首页 文章

如何使用Paypal自适应支付IPN?

提问于
浏览
5

我正在使用Paypal的自适应支付系统 . 使用沙盒帐户,我能够进行PayRequest并转发到Paypal进行付款 . 它看起来像:

Request =

2012年4月24日10:35:46 com.paypal.adaptive.api.requests.PayRequest执行INFO:发送PayRequest: requestEnvelope.errorLanguage = en_US& actionType = PAY& receiverList.receiver(0).email = seller_1334320690_biz%40email.org& receiverList.receiver(0).amount = 5.0& currencyCode = EUR& feesPayer = SENDER& cancelUrl = https%3A%2F%2Flocalhost%3A8443& returnUrl = http%3A%2F%2Flocalhost%2F& ipnNotificationUrl = http%3A%2F%2Flocalhostu%2Ffinishdeposit&

Response =

2012年4月24日10:35:48 com.paypal.adaptive.api.requests.PayPalBaseRequest makeRequest INFO:收到回复: responseEnvelope.timestamp = 2012-04-24T13%3A35%3A48.587-07%3A00& responseEnvelope.ack =成功& responseEnvelope.correlationId = c8dee8023cca6& responseEnvelope.build = 2756816& payKey = AP-1UF57245CJ360523K&paymentExecStatus = CREATED

我现在想弄清楚,我如何检查,付款已成功完成 . 所以我尝试使用沙箱工具实现ipn系统 . 但是,我不知道如何将2连接在一起 . 即,当付款时,我假设我需要在数据库中创建一个该用户已付款的记录,可能是待定/创建?然后等待ipn返回通知我已付款,并更新数据库表说完了?如何将PayRequest与IPN通知相关联,我将从paypal获得? Paypal只通过IPN-Notification发送一些信息,如:

  • item_number = AK-1234

  • residence_country =美国

  • verify_sign = ArcmaOINNZx08uC3iQY0zhEQN3IZAz70ynRk93Or8ixRi23bb4rGNIrd

  • address_country =美国

  • address_city =圣何塞

  • address_status =未经证实

  • payment_status =已完成

  • business=seller@paypalsandbox.com

  • payer_id = TESTBUYERID01

  • first_name =约翰

  • shipping = 3.04

  • payer_email=buyer@paypalsandbox.com

  • mc_fee = 0.44

  • txn_id = 484221854

  • 数量= 1

  • receiver_email=seller@paypalsandbox.com

  • notify_version = 2.1

  • txn_type = web_accept

  • test_ipn = 1

  • payer_status =已验证

  • mc_currency = USD

  • mc_gross = 12.34

  • custom = xyz123

  • mc_gross_1 = 9.34

  • payment_date = 11:54:48 2012年4月22日PDT

  • charset = windows-1252

  • address_country_code = US

  • address_zip = 95131

  • address_state = CA.

  • tax = 2.02

  • item_name =某事

  • address_name =约翰史密斯

  • last_name =史密斯

  • payment_type =即时

  • address_street = 123,任何街道

  • receiver_id = TESTSELLERID1

我无法在IPN-Notifcation中找到可用的东西 . 最好的情况是,如果我能通过付费响应获得的IPN-Notification获得相同的相关性 . 所以我可以在我的数据库中保存response-correlation-id,然后在我收到具有相同correlation-id的IPN-Notification时检查它 .

3 回答

  • 2

    他们在沙盒中给你的测试IPN很糟糕 . 看看一个真实的触发你的实际回调(甚至测试),你会看到它定义了payKey;这是你用它来查找它 .

    请注意,它们需要端口80用于IPN回调(尽管在任何地方都没有记录) .

    这是一个真实的IPN通知(翻译为JSON,特定于我的应用程序编辑的信息):

    {"payment_request_date":"Sun Jun 24 06:12:20 PDT 2012",
    "return_url":"http://redacted/paypal/transactions/3?status=completed",
    "fees_payer":"EACHRECEIVER",
    "ipn_notification_url":"http://redacted/paypal/notifications",
    "sender_email":"redacted",
    "verify_sign":"AFcWxVredacted",
    "test_ipn":"1",
    "transaction[0].id_for_sender_txn":"redacted",
    "transaction[0].receiver":"redacted",
    "cancel_url":"http://redacted/paypal/transactions/3?status=canceled",
    "transaction[0].is_primary_receiver":"false",
    "pay_key":"AP-redacted",
    "action_type":"PAY",
    "transaction[0].id":"redacted",
    "transaction[0].status":"Completed",
    "transaction[0].paymentType":"SERVICE",
    "transaction[0].status_for_sender_txn":"Completed",
    "transaction[0].pending_reason":"NONE",
    "transaction_type":"Adaptive Payment PAY",
    "transaction[0].amount":"USD 1.00",
    "status":"COMPLETED",
    "log_default_shipping_address_in_transaction":"false",
    "charset":"windows-1252",
    "notify_version":"UNVERSIONED",
    "reverse_all_parallel_payments_on_error":"true"}
    

    请注意,您必须手动在PAY请求中设置reverse_all_parallel_payments_on_error . 即使他们建议这样做(并且它可能会让你感到焦虑),但默认情况下这是假的 .

    此外,如果您错过了IPN,您可以使用PaymentDetails直接获取所有相同的信息 .

    我不知道@ swade1987在看什么,但我的IPN不包含任何有关费用金额的信息 . (这实际上就是我发现这篇文章的方式;试图找出原因.PPP API文档很可怕 . )

    其他文档可在此处找到https://developer.paypal.com/docs/classic/adaptive-payments/integration-guide/APIPN/

  • 3

    有点迟了但是对于谁从搜索引擎碰到这里...

    我最近刚开始处理Paypal API . OP引用的IPN消息是在卖家简档中定义的IPN通知URL处传递的消息 . 相反,@ sai引用的IPN是Adapative Payments IPN,传递给Pay,ExecutePaypement或Preapproval API请求中定义的ipnNotificationUrl .

    它们是两种不同类型的IPN消息,并且是documented,查找支付信息变量和支付/预批准消息变量 . 如果您同时选择这两种IPN,则可以获得这两种类型的IPN .

    关于OP引用的IPN消息,您可以使用txn_id字段的值来获取transactionId的PaymentDetails . transationId与payKey一样好,可以引用已完成的付款 .

  • 14

    这应该可以帮助你 .

    namespace Gateway
    {
        public class MerchantSellerIPNService : IMerchantSellerIPNService
        {
            /// <summary>
            /// This is the method which is hit when using the URL in the PAY request to PayPal.
            /// </summary>
            /// <param name="stream"></param>
            /// <returns></returns>
            public string ProcessIPN(Stream stream)
            {
                // Declare locally used variables.
                byte[] requestArray = null;
                string requestString = null;
                string responseString = null;
                StreamReader IPNReturnReader;
                StreamWriter streamWriter;
                MemoryStream responseStream = new MemoryStream();
                HttpWebRequest payPalRequest;
                System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    
                // Get the URL to send the IPN received back to PayPal (use either of the two below depending on the environment.)
                <add key="PAYPAL_IPN_URL" value="https://www.sandbox.paypal.com/cgi-bin/webscr" />
                <add key="PAYPAL_IPN_URL" value="https://www.paypal.com/cgi-bin/webscr"/>
    
                string IPNReturnURL = ConfigurationManager.AppSettings["PAYPAL_IPN_URL"];
    
                // Read in the data provided from PayPal
                StreamReader streamReader = new StreamReader(stream);
    
                // Obtain the email address and pre-approval key passed to use via PayPal for later use.
                string strPayPalMessage = streamReader.ReadToEnd();
    
                // Initalize the POST web request we are going to send to PayPal to valid the IPN we received from them.
                payPalRequest = (HttpWebRequest)WebRequest.Create(IPNReturnURL);
                payPalRequest.Method = "POST";
                payPalRequest.ContentType = "application/x-www-form-urlencoded";
    
                // Create an array containing the IPN message PayPal sent to us.
                requestArray = encoding.GetBytes(strPayPalMessage);
    
                // Then add the necessary string to the back to use for verfication.
                requestString = Encoding.ASCII.GetString(requestArray);
                requestString += "&cmd=_notify-validate";
                payPalRequest.ContentLength = requestString.Length;
    
                // Now write the updated IPN message back to PayPal for verification.
                streamWriter = new StreamWriter(payPalRequest.GetRequestStream(), System.Text.Encoding.ASCII);
                streamWriter.Write(requestString);
                streamWriter.Close();
    
                // Read the response from PayPal and process it.
                IPNReturnReader = new StreamReader(payPalRequest.GetResponse().GetResponseStream());
                responseString = IPNReturnReader.ReadToEnd();
                IPNReturnReader.Close();
    
                if (responseString == "VERIFIED")
                {
                    try
                    {
                        if (strPayPalMessage.Contains("payment_status=Completed"))
                        {
                            if (ProcessPaymentIPNMessage(strPayPalMessage))
                                PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessPaymentIPNMessage - Able to create new payment Transaction Detail Record"), "DEBUG");
                            else
                                PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessPaymentIPNMessage - Unable to create new payment Transaction Detail Record"), "DEBUG");
                        }
                        else if (strPayPalMessage.Contains("payment_status=Refunded"))
                        {
                            if (ProcessRefundIPNMessage(strPayPalMessage))
                                PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessRefundIPNMessage - Able to create new refund Transaction Detail Record"), "DEBUG");
                            else
                                PayPalInStore.Utils.ErrorHandler.LogError(new Exception("ProcessRefundIPNMessage - Unable to create new refund Transaction Detail Record"), "DEBUG");
                        }
                        else
                        {
                            PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - ProcessIPN - Unknown message type"), "DEBUG");
                        }
                    }
                    catch (Exception ex)
                    {
                        PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - ProcessIPN failed"), "DEBUG");
                    }
                }
                else if (responseString == "INVALID")
                {
                    PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - Invalid IPN Message Received: "), "DEBUG");
                }
                else
                {
                    PayPalInStore.Utils.ErrorHandler.LogError(new Exception("MerchantSellerIPNService - Fatal IPN Message Received: "), "DEBUG");
                }
    
                return "MerchantSellerIPNService Completed";
            }
    
            /// <summary>
            /// Method used to process the Payment IPN notification message and update the database as required.
            /// </summary>
            /// <returns></returns>
            private bool ProcessPaymentIPNMessage(string PayPalIPNMessage)
            {
                // Firstly, we need to split the IPN message into sections based on the & sign.
                string[] PayPalMessageElemetsArray = PayPalIPNMessage.Split('&');
    
                // Now obtain the list of information (from the message) we require to make the TransactionDetail record.
                string merchantTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("txn_id=", StringComparison.Ordinal));
                string feeAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_fee=", StringComparison.Ordinal));
                string grossAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_gross=", StringComparison.Ordinal));
    
                // TODO: REMOVE THIS ITS FOR DEBUGGING PURPOSES
                string errorMessage2 = String.Format("ProcessPaymentIPNMessage - merchantTransactionId: {0}, feeAmount: {1}, grossAmount: {2}", merchantTransactionId, feeAmount, grossAmount);
                PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage2), "DEBUG");
    
                try
                {
                    // We now need to remove the variable name and '=' from the elements so we only have the necessary information.
                    merchantTransactionId = merchantTransactionId.Replace("txn_id=", "");
                    feeAmount = feeAmount.Replace("mc_fee=", "");
                    grossAmount = grossAmount.Replace("mc_gross=", "");
    
                    // Now convert the values obtained from the IPN message and calculate the net amount.
                    decimal dFeeAmount = Convert.ToDecimal(feeAmount);
                    decimal dGrossAmount = Convert.ToDecimal(grossAmount);
                    decimal dNetAmount = Math.Round((dGrossAmount - dFeeAmount), 2);
    
                    try
                    {
                        // Finally create the new transaction fee record.
                        TransactionDetail transactionDetail = new TransactionDetail();
                        transactionDetail.MerchantTransactionId = merchantTransactionId;
                        transactionDetail.Gross = dGrossAmount;
                        transactionDetail.Fee = Decimal.Negate(dFeeAmount);
                        transactionDetail.Net = dNetAmount;
                        transactionDetail.TransactionType = (int)TransactionDetailTransactionType.InStorePayment;
                        transactionDetail.Save();
                    }
                    catch (Exception ex)
                    {
                        string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
                        PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
                        return false;
                    }
    
                    return true;
                }
                catch (Exception ex)
                {
                    string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
                    PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
                    return false;
                }
            }
    
            /// <summary>
            /// Method used to process the Refund IPN notification message and update the database as required.
            /// </summary>
            /// <returns></returns>
            private bool ProcessRefundIPNMessage(string PayPalIPNMessage)
            {
                // Firstly, we need to split the IPN message into sections based on the & sign.
                string[] PayPalMessageElemetsArray = PayPalIPNMessage.Split('&');
    
                // Now obtain the list of information (from the message) we require to make the TransactionDetail record.
                string merchantTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("txn_id=", StringComparison.Ordinal));
                string parentTransactionId = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("parent_txn_id=", StringComparison.Ordinal));
                string feeAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_fee=", StringComparison.Ordinal));
                string grossAmount = Array.Find(PayPalMessageElemetsArray, element => element.StartsWith("mc_gross=", StringComparison.Ordinal));
    
                try
                {
                    // We now need to remove the variable name and '=' from the elements so we only have the necessary information.
                    merchantTransactionId = merchantTransactionId.Replace("txn_id=", "");
                    parentTransactionId = parentTransactionId.Replace("parent_txn_id=", "");
                    feeAmount = feeAmount.Replace("mc_fee=", "").Replace("-", "");
                    grossAmount = grossAmount.Replace("mc_gross=", "").Replace("-", "");
    
                    // Now convert the values obtained from the IPN message and calculate the net amount.
                    decimal dFeeAmount = Convert.ToDecimal(feeAmount);
                    decimal dGrossAmount = Convert.ToDecimal(grossAmount);
                    decimal dNetAmount = Math.Round((dGrossAmount - dFeeAmount), 2);
    
                    // Now create the new transaction fee record.
                    try
                    {
                        // Finally create the new transaction fee record.
                        TransactionDetail transactionDetail = new TransactionDetail();
                        transactionDetail.MerchantTransactionId = merchantTransactionId;
                        transactionDetail.Gross = dGrossAmount;
                        transactionDetail.Fee = Decimal.Negate(dFeeAmount);
                        transactionDetail.Net = dNetAmount;
                        transactionDetail.TransactionType = (int)TransactionDetailTransactionType.InStoreRefund;
                        transactionDetail.Save();
                    }
                    catch (Exception ex)
                    {
                        string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
                        PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
                        return false;
                    }
    
                    // Finally update the PurchaseRefund record with the Parent Transaction Id (used as a backup incase the API IPN message for the payment wasn't received).
                    try
                    {
                        PurchaseRefund refund = PurchaseRefund.SingleOrDefault(x => x.RefundTransactionId == merchantTransactionId);
                        if (refund != null)
                        {
                            refund.ParentTransactionId = parentTransactionId;
                            refund.Save();
                        }
                    }
                    catch (Exception ex)
                    {
                        string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to update PurchaseRefund record (Transaction ID: {0}) with Parent Transaction Id: {1}", merchantTransactionId, parentTransactionId);
                        PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
                        return false;
                    }
    
                    // If all is succesful we can return true.
                    return true;
                }
                catch (Exception ex)
                {
                    string errorMessage = String.Format("ProcessPaymentIPNMessage - Unable to create new TransactionDetail record for Merchant Transaction ID: {0}", merchantTransactionId);
                    PayPalInStore.Utils.ErrorHandler.LogError(new Exception(errorMessage), "DEBUG");
                    return false;
                }
            }
        }
    }
    

相关问题