302 url redirect to paypal home page - paypal

We are using the PayPal AdaptivePayments SDK since a while,
Now, the same URL as before is redirecting the customer to the paypal homepage 50% of the time with a 302 temporary redirect.
https://www.paypal.com/webscr&cmd=_ap-payment&paykey=XXXXXXXXXX.
the code is mostly a copy past from the paypal sample
public override string CreateRedirectUrl(NameValueCollection parameters)
{
ReceiverList receiverList = new ReceiverList();
receiverList.receiver = new List<Receiver>();
PayRequest request = new PayRequest();
RequestEnvelope requestEnvelope = new RequestEnvelope("en_CA");
request.requestEnvelope = requestEnvelope;
request.feesPayer = "PRIMARYRECEIVER";
//request.feesPayer = "SECONDARYONLY";
Receiver receiver1 = new Receiver();
if (parameters["amount1"] != null && parameters["amount1"].Trim() != string.Empty)
{
// Required) Amount to be paid to the receiver
receiver1.amount = Convert.ToDecimal(parameters["amount1"].ToString(), CultureInfo.InvariantCulture);
}
if (parameters["mail1"] != null && parameters["mail1"].Trim() != string.Empty)
{
// Receiver's email address. This address can be unregistered with
// paypal.com. If so, a receiver cannot claim the payment until a PayPal
// account is linked to the email address. The PayRequest must pass
// either an email address or a phone number. Maximum length: 127 characters
receiver1.email = parameters["mail1"];
}
//if (parameters["primaryReceiver1"] != null && parameters["primaryReceiver1"].Trim() != string.Empty)
//{
// receiver1.primary = Convert.ToBoolean(parameters["primaryReceiver1"]);
//}
receiver1.primary = true;
receiver1.invoiceId = parameters["invoiceId"];
receiverList.receiver.Add(receiver1);
Receiver receiver2 = new Receiver();
if (parameters["amount2"] != null && parameters["amount2"].Trim() != string.Empty)
{
// (Required) Amount to be paid to the receiver
receiver2.amount = Convert.ToDecimal(parameters["amount2"], CultureInfo.InvariantCulture);
}
if (parameters["mail2"] != null && parameters["mail2"].Trim() != string.Empty)
{
// Receiver's email address. This address can be unregistered with
// paypal.com. If so, a receiver cannot claim the payment until a PayPal
// account is linked to the email address. The PayRequest must pass
// either an email address or a phone number. Maximum length: 127 characters
receiver2.email = parameters["mail2"];
}
//if (parameters["primaryReceiver2"] != null && parameters["primaryReceiver2"].Trim() != string.Empty)
//{
// receiver2.primary = Convert.ToBoolean(parameters["primaryReceiver2"]);
//}
receiverList.receiver.Add(receiver2);
ReceiverList receiverlst = new ReceiverList(receiverList.receiver);
request.receiverList = receiverlst;
// (Optional) Sender's email address. Maximum length: 127 characters
if (parameters["senderEmail"] != null && parameters["senderEmail"].Trim() != string.Empty)
{
request.senderEmail = parameters["senderEmail"];
}
// The action for this request. Possible values are: PAY – Use this
// option if you are not using the Pay request in combination with
// ExecutePayment. CREATE – Use this option to set up the payment
// instructions with SetPaymentOptions and then execute the payment at a
// later time with the ExecutePayment. PAY_PRIMARY – For chained
// payments only, specify this value to delay payments to the secondary
// receivers; only the payment to the primary receiver is processed.
//if (parameters["actionType"] != null && parameters["actionType"].Trim() != string.Empty)
//{
// request.actionType = parameters["actionType"];
//}
request.actionType = "PAY";
// URL to redirect the sender's browser to after canceling the approval
// for a payment; it is always required but only used for payments that
// require approval (explicit payments)
if (parameters["cancelURL"] != null && parameters["cancelURL"].Trim() != string.Empty)
{
request.cancelUrl = parameters["cancelURL"];
}
// The code for the currency in which the payment is made; you can
// specify only one currency, regardless of the number of receivers
if (parameters["currencyCode"] != null && parameters["currencyCode"].Trim() != string.Empty)
{
request.currencyCode = parameters["currencyCode"];
}
// URL to redirect the sender's browser to after the sender has logged
// into PayPal and approved a payment; it is always required but only
// used if a payment requires explicit approval
if (parameters["returnURL"] != null && parameters["returnURL"].Trim() != string.Empty)
{
request.returnUrl = parameters["returnURL"];
}
request.requestEnvelope = requestEnvelope;
// (Optional) The URL to which you want all IPN messages for this
// payment to be sent. Maximum length: 1024 characters
if (parameters["ipnNotificationURL"] != null && parameters["ipnNotificationURL"].Trim() != string.Empty)
{
request.ipnNotificationUrl = parameters["ipnNotificationURL"];
}
AdaptivePaymentsService service = null;
try
{
// Configuration map containing signature credentials and other required configuration.
// For a full list of configuration parameters refer in wiki page
// (https://github.com/paypal/sdk-core-dotnet/wiki/SDK-Configuration-Parameters)
Dictionary<string, string> configurationMap = PaypalAdaptiveConfiguration.GetAcctAndConfig();
// Creating service wrapper object to make an API call and loading
// configuration map for your credentials and endpoint
service = new AdaptivePaymentsService(configurationMap);
Response = service.Pay(request);
}
catch (System.Exception ex)
{
// contextHttp.Response.Write(ex.Message);
//return;
}
Dictionary<string, string> responseValues = new Dictionary<string, string>();
string redirectUrl = null;
if (!Response.responseEnvelope.ack.ToString().Trim().ToUpper().Equals(AckCode.FAILURE.ToString()) && !Response.responseEnvelope.ack.ToString().Trim().ToUpper().Equals(AckCode.FAILUREWITHWARNING.ToString()))
{
if (ConfigurationManager.AppSettings["PAYPAL_MODE"].ToLower() == "live")
{
redirectUrl = ConfigurationManager.AppSettings["PAYPAL_REDIRECT_URL_LIVE"] + "_ap-payment&paykey=" + Response.payKey;
}
else
{
redirectUrl = ConfigurationManager.AppSettings["PAYPAL_REDIRECT_URL"] + "_ap-payment&paykey=" + Response.payKey;
}
// The pay key, which is a token you use in other Adaptive Payment APIs
// (such as the Refund Method) to identify this payment.
// The pay key is valid for 3 hours; the payment must be approved while the
// pay key is valid.
responseValues.Add("Pay Key", Response.payKey);
// The status of the payment. Possible values are:
// CREATED – The payment request was received; funds will be transferred once the payment is approved
// COMPLETED – The payment was successful
// INCOMPLETE – Some transfers succeeded and some failed for a parallel payment or, for a delayed chained payment, secondary receivers have not been paid
// ERROR – The payment failed and all attempted transfers failed or all completed transfers were successfully reversed
// REVERSALERROR – One or more transfers failed when attempting to reverse a payment
// PROCESSING – The payment is in progress
// PENDING – The payment is awaiting processing
responseValues.Add("Payment Execution Status", Response.paymentExecStatus);
if (Response.defaultFundingPlan != null && Response.defaultFundingPlan.senderFees != null)
{
// Fees to be paid by the sender
responseValues.Add("Sender Fees", Response.defaultFundingPlan.senderFees.amount + Response.defaultFundingPlan.senderFees.code);
}
}
foreach (ErrorData ed in Response.error)
{
this.ErrorMessage += ed.message + " ";
}
if (!string.IsNullOrWhiteSpace(this.ErrorMessage))
{
new Tracker().sendToGenclikDev("PaypalAdaptiveGateway", this.ErrorMessage);
}
responseValues.Add("Acknowledgement", Response.responseEnvelope.ack.ToString().Trim().ToUpper());
return redirectUrl;
}

We also had same issue but were able to fix it by making a small change.
You need to change the redirect URL:
From this:
https://www.paypal.com/webscr&cmd=_ap-payment&paykey=XXXXXXXXXX
To:
https://www.paypal.com/webscr?cmd=_ap-payment&paykey=XXXXXXXXXX
Note the "?" after 'webscr'.

We are having exactly the same problem since Friday the 13th Jan 2017. It appears to mostly happen on the first payment redirect request. If you navigate back from the Paypal home screen, using the browser's back button and post again with a new token, it works.
Anybody that knows a solution would be of great help!

Related

Paypal SDK giving error when trying to create Payment

I created an app on PayPal with sandbox account. Then i picked up its API Client id and Secret.
I created a new console application and added paypal nuget package version 1.9.1.
internal class Program { static void Main(string[] args) { // Get a reference to the config var config = ConfigManager.Instance.GetProperties();
// Use OAuthTokenCredential to request an access token from PayPal
var accessToken = new OAuthTokenCredential(config["clientId"], config["clientSecret"]);
var apiContext = new APIContext(accessToken.GetAccessToken());
try
{
var payment = new Payment()
{
intent = "authorize",
// A resource representing a Payer that funds a payment. Use the List of `FundingInstrument` and the Payment Method as 'credit_card'
payer = new Payer()
{
// The Payment creation API requires a list of
// FundingInstrument; add the created `FundingInstrument`
// to a List
funding_instruments = new List<FundingInstrument>()
{
// A resource representing a Payeer's funding instrument.
// Use a Payer ID (A unique identifier of the payer generated
// and provided by the facilitator. This is required when
// creating or using a tokenized funding instrument)
// and the `CreditCardDetails`
new FundingInstrument()
{
// A resource representing a credit card that can be used to fund a payment.
credit_card = new CreditCard()
{
billing_address = new Address()
{
city = "Johnstown",
country_code = "US",
line1 = "52 N Main ST",
postal_code = "43210",
state = "OH"
},
cvv2 = "874",
expire_month = 11,
expire_year = 2023,
first_name = "Joe",
last_name = "Shopper",
number = "5105105105105100",
type = "visa"
}
}
},
payment_method = "credit_card"
},
// The Payment creation API requires a list of transactions; add the created `Transaction` to a List
transactions = new List<Transaction>()
{
// A transaction defines the contract of a payment - what is the payment for and who is fulfilling it. Transaction is created with a `Payee` and `Amount` types
new Transaction()
{
// Let's you specify a payment amount.
amount = new Amount()
{
currency = "USD",
// Total must be equal to sum of shipping, tax and subtotal.
total = "107.47",
details = new Details()
{
shipping = "0.03",
subtotal = "107.41",
tax = "0.03"
}
},
description = "This is the payment transaction description."
}
},
payee = new Payee { email = "rassmasood#hotmail.com" }
};
var createdPayment = payment.Create(apiContext);
}
catch (Exception ex)
{
throw;
}
}
}
Using the code above i get the error mentioned below.
{"name":"VALIDATION_ERROR","message":"Invalid request - see details","debug_id":"f3c178cb2a17b","details":[{"field":"/payee","location":"body","issue":"MALFORMED_REQUEST_JSON"}],"links":[]}
is there anything i am doing wrong ?
i tried adding more information to Payee for example its account merchand_id and stuff but didnt worked.
i am expecting the exception to be resolved.
The package you are using is 5 years old and deprecated.
Use the current PayPalCheckoutSDK 1.0.4

Integrating with PayPal with no web site

I was thinking about integration in the background without any website other than the payment page as part of a desktop application in c++.
Would it be possible to following the following scenario:
1. Generate the invoice / sale and via REST API obtain some sort of unique ID for the transaction to come.
2. Redirect to Paypal web site to a ad-hoc payment page, using the unique ID.
3. In the background, check every few minutes, via REST API, if the payment was made.
We finally found a way, which is why I publish this answer along with some code (a POC) we have developed. This is a POC for a built-in payment processing engine which allows you to accept payments from any credit card holder (regardless of being a PayPal customer) and pay for unlocking a software product or for specific features.
To process payments you need to apply as a PayPal developer and obtain your own PayPal credentials. You will then receive 2 sets of credentials. One for tests ("sandbox") and the other for real life.
First you can use the Sandbox to test the API
Void InitPayPal(BOOL Sandbox, LPTSTR User, LPTSTR password, LPTSTR signature, LPTSTR successUrl, LPTSTR failedURL)
Sandbox – indicates whether you are testing your integration using PayPal's Sandbox account, or going live.
User – your PayPal user name
Password – your PayPal password
Signature – you PayPal signature
successUrl – a url leading to a web page which you wish to be shown after successful payment.
failedURL – a url leading to a web page which you wish to be shown after failed / cancalled payment.
This function is straight forward:
void InitPayPal(BOOL Sandbox, LPTSTR User, LPTSTR password, LPTSTR signature, LPTSTR successUrl, LPTSTR failedURL, LPWSTR ProductName)
{
m_sandbox = Sandbox;
m_user = User;
m_password = password;
m_signature = signature;
m_SuccessURL = successUrl;
m_FailureURL = failedURL;
m_ProductName = ProductName;
CUR_CHAR = L"$";
SYSTEMTIME st;
GetSystemTime(&st);
g_tPayStart = CTime(st);
InitilizedPaypal = TRUE;
}
Initiating a payment
When you wish to initiate a payment from your program, you call the following function which I wrote which generally build a string (ExpChkoutStr) and use the following PayPal API call:
// Send string to PayPal server
WinHttpClient WinClient1(ExpChkoutStr.GetBuffer());
WinClient1.SetRequireValidSslCertificates(false);
WinClient1.SendHttpRequest(L"GET");
httpResponseContent1 = WinClient1.GetResponseContent();
CString strTransactionRet = UrlDecode(httpResponseContent1.c_str());
The WinHTTP class was developed by Cheng Shi.
The Express Checkout String (ExpChkoutStr) is generated by another function which uses the member variables' values and the transaction details into a single string:
CString result;
result = (m_sandbox) ? PAYPAL_SANDBOX_HTTPS : PAYPAL_REAL_HTTPS;
result += Q_USER;
result += m_user;
result += AND_PASSWORD;
result += m_password;
result += AND_SIGNATURE;
result += m_signature;
result += AND_PAYMENTAMOUNT;
result += strAmount;
result += L"&METHOD=SetExpressCheckout";
result += AND_RETURN_URL;
result += m_SuccessURL;
result += AND_CANCEL_URL;
result += m_FailureURL;
result += AND_VERSION;
result += L"&NOSHIPPING=1";
result += L"&ADDROVERRIDE=0&BRANDNAME=Secured Globe, Inc.";
result += L"&PAYMENTREQUEST_0_DESC=";
result += L"Item name: " + strUnits + L"(" + UnitName + L") ";
result += L"Price: " + strAmount;
result += L"&NOTETOBUYER=Here you can add a note to the buyer";
The result from the PayPal server is a "token" used to figure out a one-time web page (LinkToOpen ) that must be opened in order for the end user to confirm the purchase:
// Extract token from response
CString sToken = ExtractElement(strTransactionRet, L"TOKEN");
if (sToken == L"")
{
wprintf(L"Internal error: (Paypal): no token was generated (%s)", strTransactionRet);
MessageBox(NULL, L"Internal payment processing error", L"", MB_OK);
return FALSE;
}
CString LinkToOpen = (m_sandbox) ? SANDBOX_PAYPAL_CHECKOUT : REAL_PAYPAL_CHECKOUT;
LinkToOpen += L"&token=";
LinkToOpen += sToken;
We then programatically open this one-time web page using the default web browser:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CString command_line;
command_line.Format(L"cmd.exe /c start \"link\" \"%s\" ", LinkToOpen);
// LinkToOpen
if (!CreateProcess(NULL, // No module name (use command line)
command_line.GetBuffer(),
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
wprintf(L"CreateProcess failed (%d).\n", GetLastError());
// At this stage you would want to mark this transaction as "failed"
return FALSE;
}
Then the rest is to maintain a small database of all pending transactions and follow up each of them until it is either succeed, failed, cancelled or if a timeout has passed.
To extract elements from the PayPal server response, we wrote this small function:
CString ExtractElement(CString EntireString, CString ElementName)
{
CString result = L"";
CString WhatToFind = ElementName + L"=";
int foundToken = EntireString.Find(WhatToFind);
if (foundToken > -1)
{
int EndToken = EntireString.Find(L"&", foundToken);
if (EndToken != -1)
{
result = EntireString.Mid(foundToken + ElementName.GetLength()+1, EndToken - foundToken - ElementName.GetLength()-1);
}
}
return result;
}

Invalid Sandbox Credit Card # for Paypal

I've downloaded the Paypal SDK for android and have compared my code with the Example Project and they are identical except for the added intents I send to the Activity after paypal. The problem is I'm getting a weird error. Once I click the button, and add cc details and press charge...paypal says the credit card I'm entering is invalid. This only happens when I try to send my own extra stuff through. When I remove my extra intents suddenly the same sandbox credit card goes through just fine. Is this because paypal only accept certain variables, maybe Floats and doesn't like strings? Or, maybe there is a simple flaw in my code?
public void onBuyPressed(View pressed) {
TextView inputx =(TextView)findViewById(R.id.super);
String ix =inputx.getText().toString();
TextView inputP =(TextView)findViewById(R.id.input);
String ip =inputP.getText().toString();
try{
double valuey =Double.parseDouble(ip);
if(valuey>0)
{
PayPalPayment payment = new PayPalPayment(new BigDecimal(valuey), "USD", ix );
Intent intent = new Intent(this, PaymentActivity.class);
intent.putExtra(PaymentActivity.EXTRA_PAYPAL_ENVIRONMENT, PaymentActivity.ENVIRONMENT_SANDBOX);
// it's important to repeat the clientId here so that the SDK has it if Android restarts your
// app midway through the payment UI flow.
intent.putExtra(PaymentActivity.EXTRA_CLIENT_ID, "AZetc,etc,etc..the Id is NOT the issue");
// Provide a payerId that uniquely identifies a user within the scope of your system,
// such as an email address or user ID.
intent.putExtra(PaymentActivity.EXTRA_PAYER_ID, "");
intent.putExtra(PaymentActivity.EXTRA_RECEIVER_EMAIL,"myprivateemail#yahoo.com");
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment);
startActivityForResult(intent, 0);
}
else{
Toast.makeText(getApplicationContext(), "You haven't entered anything.",
Toast.LENGTH_LONG).show();
}} catch (NumberFormatException e) {
}}
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
PaymentConfirmation confirm = data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
if (confirm != null) {
final TextView x =(TextView)findViewById(R.id.custname);
String onex = x.getText().toString();
final TextView nov =(TextView)findViewById(R.id.secret);
String dec = nov.getText().toString();
final TextView feb =(TextView)findViewById(R.id.secretname);
String jan = feb.getText().toString();
final TextView xx =(TextView)findViewById(R.id.inputPrice);
String twox = xx.getText().toString();
final TextView xxx =(TextView)findViewById(R.id.help);
String threex = xxx.getText().toString();
Intent intent= new Intent(SuperActivity2.this,SuperCheckActivity.class)
.putExtra("secret",dec)
.putExtra("secretname",jan)
.putExtra("date",threex)
.putExtra("inputPrice",twox)
.putExtra("customername",onex)
.putStringArrayListExtra("list", listItems);
startActivity(intent);
try {
Log.i("paymentExample", confirm.toJSONObject().toString(4));
// TODO: send 'confirm' to your server for verification.
// see https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
// for more details.
} catch (JSONException e) {
Log.e("paymentExample", "an extremely unlikely failure occurred: ", e);
}
}
}
else if (resultCode == Activity.RESULT_CANCELED) {
Log.i("paymentExample", "The user canceled.");
}
else if (resultCode == PaymentActivity.RESULT_PAYMENT_INVALID) {
Log.i("paymentExample", "An invalid payment was submitted. Please see the docs.");
}
I have the same problem. When i try to pay with PayPal goes OK, but when i try to pay with Credit Card i get the error, that data for credit card are incorrect.
Then i debug my app and see, that amount which i set to PayPalPayment have large percision. So i set my percision to 2 decimal place and paying with credit card works OK.
Here is example, how i set amount:
ttlPrice = new BigDecimal(totalPrice, MathContext.DECIMAL32).setScale(2);
payment = new PayPalPayment(ttlPrice, "EUR", "description");
ttlPrice = new BigDecimal(totalPrice, MathContext.DECIMAL32).setScale(2);
payment = new PayPalPayment(ttlPrice, "EUR", "description");

paypal masspay allways returning success, even with inexisting users

I'm currently testing my paypal integration. When using Masspay, every user name that I enter who hace a mail-like format(an # and a ".") I get a "success" response. I'm using the following code:
def emails =[]
def amounts = []
emails[0]=params.cuenta;
amounts[0]=params.cantidad;
//masspay.massPayCode("Dinero recibido de TikCode.com", "info#tikcode.com", emails, amount, "EUR");
String emailSub = "Dinero recibido de TikCode.com";
String emailAddress = "info#tikcode.com";
String [] receiverEmailItems = emails;
String [] amountItems = amounts;
String currencyCode = "EUR";
CallerServices caller = new CallerServices();
String responseValue = null;
APIProfile profile = ProfileFactory.createSignatureAPIProfile();
/*
WARNING: Do not embed plaintext credentials in your application code.
Doing so is insecure and against best practices.
Your API credentials must be handled securely. Please consider
encrypting them for use in any production environment, and ensure
that only authorized individuals may view or modify them.
*/
// Set up your API credentials, PayPal end point, and API version.
profile.setAPIUsername("myusername");
profile.setAPIPassword("mypasswordapi");
//profile.setSignature("AFcWxV21C7fd0v3bYYYRCpSSRl31ARykt6evANuQsOANN9TjehZjqIl3");
profile.setSignature("mysignature");
profile.setEnvironment("sandbox");
caller.setAPIProfile(profile);
MassPayRequestType pprequest = new MassPayRequestType();
pprequest.setVersion("90.0");
println("llegas aqui?")
// Add request-specific fields to the request.
MassPayRequestItemType[] massPayItem = new MassPayRequestItemType[receiverEmailItems.length];
int j = 0
for(int i=0;i<receiverEmailItems.length; i++)
{
String recreceiverEmail=receiverEmailItems[i];
if(recreceiverEmail != null && recreceiverEmail.length()!= 0)
{
MassPayRequestItemType massItemReq = new MassPayRequestItemType();
massItemReq.setReceiverEmail(receiverEmailItems[i]);
BasicAmountType amount = new BasicAmountType();
amount.set_value(amountItems[i]);
amount.setCurrencyID(CurrencyCodeType.fromString(currencyCode));
//massItemReq.setUniqueId(uniqueIdItems[i]);
//massItemReq.setNote(noteItems[i]);
massItemReq.setAmount(amount);
massPayItem[j]=massItemReq;
j++;
}
}
pprequest.setEmailSubject(emailSub);
// pprequest.setReceiverType(ReceiverInfoCodeType.fromString("abdel#publidirecta.com"));
pprequest.setMassPayItem(massPayItem);
// Execute the API operation and obtain the response.
MassPayResponseType ppresponse = (MassPayResponseType) caller.call("MassPay", pprequest);
responseValue = ppresponse.getAck().toString();
// println(ex)
// ex.printStackTrace();
return responseValue;
That's as intended; MassPay allows money to be transferred to non-registered users.
These people will then receive an email from PayPal, letting them know that money is waiting for them, and they'll just need to sign up to PayPal and confirm their email address to receive it.
The money will then be available to be withdrawn to a bank account, or spent as PayPal balance.

How to use Paypal adaptive payments with IPN?

I am using the adaptive payment system from Paypal. Using a sandbox account, I was able to make a PayRequest and get forwarded to Paypal to do the payment.
It's then looking like:
Request=
Apr 24, 2012 10:35:46 PM com.paypal.adaptive.api.requests.PayRequest execute
INFO: Sending PayRequest with: 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=
Apr 24, 2012 10:35:48 PM com.paypal.adaptive.api.requests.PayPalBaseRequest makeRequest
INFO: Received Response: responseEnvelope.timestamp=2012-04-24T13%3A35%3A48.587-07%3A00&responseEnvelope.ack=Success&responseEnvelope.correlationId=c8dee8023cca6&responseEnvelope.build=2756816&payKey=AP-1UF57245CJ360523K&paymentExecStatus=CREATED
I'm now trying to figure out, how i can check, the payment was successfully completed.
So I tried to implement the ipn system, which works using the sandbox tools.
However, I don't know how to connect the 2 together. i.e. when a payment is made, I am assuming I need to create a record in a database that this user has made a payment, probably as pending/created?
Then wait for the ipn to return to notify me that the payment is made, and update the database table to say complete?
How can i correlate the PayRequest to the IPN-Notification, i'll get from paypal? Paypal is only sending a few information with the IPN-Notification like:
item_number=AK-1234
residence_country=US
verify_sign=ArcmaOINNZx08uC3iQY0zhEQN3IZAz70ynRk93Or8ixRi23bb4rGNIrd
address_country=United States
address_city=San Jose
address_status=unconfirmed
payment_status=Completed
business=seller#paypalsandbox.com
payer_id=TESTBUYERID01
first_name=John
shipping=3.04
payer_email=buyer#paypalsandbox.com
mc_fee=0.44
txn_id=484221854
quantity=1
receiver_email=seller#paypalsandbox.com
notify_version=2.1
txn_type=web_accept
test_ipn=1
payer_status=verified
mc_currency=USD
mc_gross=12.34
custom=xyz123
mc_gross_1=9.34
payment_date=11:54:48 Apr 22, 2012 PDT
charset=windows-1252
address_country_code=US
address_zip=95131
address_state=CA
tax=2.02
item_name=something
address_name=John Smith
last_name=Smith
payment_type=instant
address_street=123, any street
receiver_id=TESTSELLERID1
I cant find something usable in that IPN-Notifcation. The best would be if i could get the same correlation-id with the IPN-Notification i already got with the pay-response. So i could save the response-correlation-id on my database and then check against it if i receive the IPN-Notification with the same correlation-id.
The test IPN they give you in the sandbox is terrible. Look at a real one triggered to your actual callback (even a test), and you'll see it has the payKey defined; this is what you use to look it up.
Note that they require port 80 for the IPN callback (though that's not documented anywhere).
Here's a real IPN notification (translated to JSON, info specific to my app redacted):
{"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"}
Note that you have to set reverse_all_parallel_payments_on_error in the PAY request manually. Even though they recommend doing so (and it'll probably save you angst) it's false by default.
Also, you can use PaymentDetails to get all the same info directly if you miss the IPN.
I don't know what #swade1987 was looking at, but my IPNs don't include any info about the fee amount. (That's actually how I found this post; trying to figure out why. The PP API documentation is horrid.)
Additional documentation can be found here https://developer.paypal.com/docs/classic/adaptive-payments/integration-guide/APIPN/
A bit late but for whoever bumps into here from a search engine...
I just started dealing with Paypal APIs myself lately. The IPN message the OP is quoting is the one delivered at the IPN notification URL defined in the seller profile. In contrast, the IPN quoted by #sai, is the Adapative Payments IPN, delivered to the ipnNotificationUrl defined in the Pay, ExecutePaypement or Preapproval API requests.
They are two different types of IPN messages and are documented, look for Payment Information Variables and Pay/Preapproval Message Variables. You can get both types of IPNs if you opt for both of them.
Concerning the IPN message quoted by the OP, you can use the value of txn_id field to get PaymentDetails by transactionId. The transationId is as good as the payKey to reference a completed payment.
This should help you massively.
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;
}
}
}
}