我正在使用Paypal的自适应支付系统。使用沙盒帐户,我能够进行PayRequest并转发到Paypal进行付款。 它看起来像是:
请求 =
2012年4月24日下午10:35:46 com.paypal.adaptive.api.requests.PayRequest执行 信息:发送PayRequest: requestEnvelope.errorLanguage = en_US& actionType = PAY& receiverList.receiver(0).email = seller_1334320690_biz%40email。有机&安培;的 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&安培;
响应 =
2012年4月24日下午10:35:48 com.paypal.adaptive.api.requests.PayPalBaseRequest makeRequest 信息:收到响应: 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发送一些信息,如:
我无法在IPN-Notifcation中找到可用的东西。最好的情况是,如果我能通过付费响应得到的IPN-Notification获得相同的相关性。所以我可以在我的数据库中保存response-correlation-id,然后在我收到具有相同corre-id的IPN-Notification时检查它。
答案 0 :(得分:14)
他们在沙盒中为您提供的测试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/
答案 1 :(得分: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一样好,可以引用已完成的付款。
答案 2 :(得分:2)
这应该可以帮助你。
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;
}
}
}
}