How to use Paypal adaptive payments with IPN? - paypal

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;
}
}
}
}

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

Send Money to Paypal Account ASP.Net Server Side Code

I am having a difficult time finding halfway descent documentation or examples on how to send money to another Paypal account.
I have installed the Nuget package PaypalSDK version 1.0.4. I have read the documentation at https://developer.paypal.com/home. I have browsed and tried to implement the sample code at https://github.com/paypal/Checkout-NET-SDK.
The problem I am having is that I am having is that I am not seeing notifications of payments sent or received in my sandbox account. I can successfully execute a checkout with the Javascript button in my shopping cart view. But eventually I want to add the capability to send money from my Paypal business account to another Paypal business account, without the other Paypal Business Account owner having to be logged in to my website.
Does the money recipient have to authorize the money I send, or should it just get deposited into their account once I send it?
Here is my code:
namespace MyShoppingCart.Helpers.Paypal
{
public class CaptureOrderSample
{
static string PayPalClientID = Startup.StaticConfig.GetValue<string>("Paypal:ClientID");
static string PayPalClientSecret = Startup.StaticConfig.GetValue<string>("Paypal:ClientSecret");
public static HttpClient client()
{
// Creating a sandbox environment
PayPalEnvironment environment = new SandboxEnvironment(PayPalClientID, PayPalClientSecret);
// Creating a client for the environment
PayPalHttpClient client = new PayPalHttpClient(environment);
return client;
}
public async static Task<HttpResponse> createOrder(string Email)
{
HttpResponse response;
// Construct a request object and set desired parameters
// Here, OrdersCreateRequest() creates a POST request to /v2/checkout/orders
var order = new OrderRequest()
{
CheckoutPaymentIntent = "CAPTURE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = "100.00"
},
Payee = new Payee
{
Email = Email // "payee#email.com"
}
}
}
//,
//ApplicationContext = new ApplicationContext()
//{
// ReturnUrl = "https://www.example.com",
// CancelUrl = "https://www.example.com"
//}
};
// Call API with your client and get a response for your call
var request = new OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(order);
response = await client().Execute(request);
var statusCode = response.StatusCode;
Order result = response.Result<Order>();
Debug.WriteLine($"Status: {result.Status}");
Debug.WriteLine($"Order Id: {result.Id}");
Debug.WriteLine($"Intent: {result.CheckoutPaymentIntent}");
Debug.WriteLine("Links:");
foreach (LinkDescription link in result.Links)
{
Debug.WriteLine($"\t{link.Rel}: {link.Href}\tCall Type: { link.Method}");
}
return response;
}
}
}
And this is currently called from my Orders controller when an order is completed. This is just for testing purposes.
[Authorize]
public async Task<IActionResult> CompleteOrder()
{
var items = _shoppingCart.GetShoppingCartItems();
Models.Order order = await _ordersService.StoreOrderAsync(items);
PrepareSellerEmail(items, order, "You Have a New Order!");
PrepareBuyerEmail(items, order, "Thank You for Your Order!");
await _shoppingCart.ClearShoppingCartAsync(_serviceProvider);
DeleteCartIDCookie();
//OrderRequest request = Helpers.CreateOrderSample.BuildRequestBody("USD", "100.00", "sb-r43z1e9186231#business.example.com");
//var client = Helpers.Paypal.CaptureOrderSample.client();
var result = Helpers.Paypal.CaptureOrderSample.createOrder("sb-r43z1e9186231#business.example.com");
//var response = await PayPalClient.client().execute.(request);
return View("OrderCompleted");
}
The output of the result is:
Status: CREATED
Order Id: 51577255GE4475222
Intent: CAPTURE
Links:
self: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222 Call Type: GET
approve: https://www.sandbox.paypal.com/checkoutnow?token=51577255GE4475222 Call Type: GET
update: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222 Call Type: PATCH
capture: https://api.sandbox.paypal.com/v2/checkout/orders/51577255GE4475222/capture Call Type: POST
This is a screen capture from my sandbox account:
Am I supposed to do something else to actually execute the transfer?
Edit: I figured out how to use the Paypal Payouts API.
First I installed the Nuget Package. It's simply called PayoutsSdk. I'm using version 1.1.1.
For the payout to execute, you need the client() method that is listed above in this post, and this CreatePayout() method listed below.
public async static Task<HttpResponse> CreatePayout()
{
var body = new CreatePayoutRequest()
{
SenderBatchHeader = new SenderBatchHeader()
{
EmailMessage = "Congrats on recieving 1$",
EmailSubject = "You recieved a payout!!"
},
Items = new List<PayoutItem>()
{
new PayoutItem()
{
RecipientType="EMAIL",
Amount=new Currency()
{
CurrencyCode="USD",
Value="1",
},
Receiver="sb-r43z1e9186231#business.example.com",
}
}
};
PayoutsPostRequest request = new PayoutsPostRequest();
request.RequestBody(body);
var response = await client().Execute(request);
var result = response.Result<CreatePayoutResponse>();
Debug.WriteLine($"Status: {result.BatchHeader.BatchStatus}");
Debug.WriteLine($"Batch Id: {result.BatchHeader.PayoutBatchId}");
Debug.WriteLine("Links:");
foreach (PayoutsSdk.Payouts.LinkDescription link in result.Links)
{
Debug.WriteLine($"\t{link.Rel}: {link.Href}\tCall Type: {link.Method}");
}
return response;
}
Of course I'll add parameters to the method for email, amount, currency code, email message, and subject.
Right now, I am calling this method from the controller method like this: var result = Helpers.Paypal.CaptureOrderSample.CreatePayout(); where Helpers.Paypal are folders that contain a class called CaptureOrderSample, which I will probably rename.
To send money from your account to another account, there are several different options:
Automate the sending with the Payouts API or Payouts Web (spreadsheet upload). For live, payouts can only be used if the live account sending the payment is approved for payouts.
Log into the account that is going to send the money in https://www.paypal.com or https://www.sandbox.paypal.com and click on the menu for Pay & Get Paid -> Send Money .
Use a PayPal Checkout integration, with or without the Orders API, and specify a payee that is to receive the money. You must log in with the paying (sending) account to approve the sending, and finally the order must be captured (via API or client side actions.order.capture()) which is what results in a PayPal transaction. If the final capture step is not performed, no money will be sent and the order will merely remain created or approved and eventually expire (72 hours after creation or 3 hours after approval)
In the sandbox, no actual emails are sent with notifications. Instead, the developer.paypal.com dashboard has a "Notifications" tab on the left, and of course activity will also be visible in each sandbox account by logging into the account. Only captured activity is likely to be visible.

302 url redirect to paypal home page

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!

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.