The Situation: My company wants to enable donations through their website. There are two ways a user can donate. 1. Technology. 2. Invest in a project.
The Set-Up: I am using Gravity Forms and PayPal Payments Pro add-on for payment processing on the website. ('Working Great.')
My Issue: I'de like to ensure that payments are labeled when they are passed to my PayPal account. I.E I want to know when someone donates where that money should go. Although all payments will go to one account. In paypal pro you can add comments to transactions. I need to be able to automatically add the comment when a form is submitted.
The solution: Use the below code to add a comment filter to gravity forms pay pal pro. So when the payment is processed for a given form, it also appears in my paypal account with a comment.
add_filter( 'gform_paypalpaymentspro_args_before_payment','add_donation_comment', 10, 2 );
function add_donation_comment( $args, $form_id ) { // do not edit $args or $form_id
// apply to form 1 only
if ( 1 == $form_id ) { // Replace '1' with your form id
$args["COMMENT1"] = 'Field Project'; // Comment1 is the arg that paypal pro uses.
}
// apply to form 2 only
if ( 2 == $form_id ) { // Replace '2' with your form id
$args["COMMENT1"] = 'Help Us Grow';
}
// always return the args, even if not changing anything
return $args;
}
Related
Of the three files here- https://github.com/paypal/ipn-code-samples/tree/master/php
I have my Webhook URL set to the stock github version of- PaypalIPN.php (this validates successfully 100% of the time, if I use example_usage.php... Doesn't work. If I use both as Webhooks... Doesn't work).
From the Paypal button side of things I'm able to post my website's active user (call him $MrUser) with this:
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
"custom_id":"<?php echo $MrUser; ?>",
"description":"One hundred Webdollars",
"amount":
{
"currency_code":"USD",
"value":1.99
}
}]
});
},
Here's the SQL I need to run upon successful validation (I change $MrUser to $kitty for clarity's sake):
require 'sqlconfig.php';
$dsn = "mysql:host=$host;dbname=$db;charset=UTF8";
try {
$pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo $e->getMessage();
}
$hashedIP = $_SERVER['REMOTE_ADDR'];
$kitty = $_POST['custom']; // Not sure this is working yet, but this should give me $mrUser;
$sql = "INSERT INTO `Insert_upon_Paypal_success` (`date`,`hashedIP`,`username`,`webdollarAMT`) VALUES (now(),:hashedIP,:kitty,'100')";
$statement = $pdo->prepare($sql);
$statement->bindValue(':hashedIP', $hashedIP);
$statement->bindValue(':kitty', $kitty);
$inserted = $statement->execute();
I'm popping this into the PaypalIPN.php file upon validation, but, it doesn't work. Here's how I have it in there:
// Check if PayPal verifies the IPN data, and if so, return true.
if ($res == self::VALID) {
return true;
// i.e. putting all of the SQL above right here.
} else {
return false;
}
I'm guessing I need to put the SQL in a specific place that I'm missing, as per the layout of the PaypalIPN.php file... Please help!!
There is no reason to use IPN with current PayPal Checkout integrations. It is very old technology (20+ years) and should be deprecated soon.
Webhooks are a successor to IPN. However, even they are unnecessary for normal payment processing -- better used only if you need automated notifications of post-checkout exceptions such as refunds or disputes.
For normal PayPal payments, do not use either.
Instead, use the v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use the (recently deprecated) Checkout-PHP-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call with PHP's curl or similar. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)
I have multiple websites that use PayPal Buy Now buttons and have PDT enabled.
All of the existing customer websites are working, in that after the user makes a purchase, they are redirected back to their site by PayPal with a return URL that includes the 'cm' parameter as specified here.
I have a new client with a brand new Merchant Account who has their PDT configured correctly (I have checked it multiple times to be sure). However, upon return from PayPal payment, we are missing the 'cm' parameter that is necessary to do validation/updates on our website.
I have never seen this before where some of the variables are getting sent back, but not all as defined in the PayPal documentation above.
Here is an example of a working return URL - (some values edited for privacy purposes only)
.../paypal/pdt?redirect=https://journals.myclient.com/view/journals/cssm/4/1/article-p14.xml?PFTxId=4435&offerProvider=DEFAULT&amt=9.95&cc=USD&cm=mJ5v4sm1PUcD0E9vbii0pm6e1ql5GRs/lv+aQuNuves=%7CaccountId=XXXXX%7COffer ID=7|mc_gross=9.95&item_name=ITEM NAME Dilemma&item_number=/journals/cssm/4/1/article-p14.xml&st=Completed&tx=XXXXXXXXXXXX
Here is the example of the newly created Merchant Account where this is not working
.../paypal/pdt?redirect=https://www.nonworkingclient.org/view/journals/tpmd/s1-1/6/article-p331.xml?PFTxId=40&offerProvider=DEFAULT&PayerID=RPUJELM94HEYU&st=Completed&tx=XXXXXXXXXX&cc=USD&amt=0.01
Here you can see in the comparison, PayPal is returning the 'tx' and 'cc' variables in both examples, but 'cm' is missing from the bottom example.
Has anyone else experienced this lately?
Does anyone know of anything more than the PDT setup that needs to be checked to see why this is failing in the Merchant Account?
Thank you for any assistance.
I have an account that has been set up and working for 3+ years. PDT always returned a "cm" parameter which I pass in as "custom". In the last couple weeks it is hit or miss whether I get "cm" back (can't say exactly when it stopped working as the site has not been used since Spring 2020). About 10% of the time it works but the other 90% no "cm". The response is a bit different in the success and failure cases. Here is an example of a successful return with "cm" (the output is the GET array parameters and values)
(
[feepaid] => Y
[amt] => 75.00
[cc] => USD
[cm] => 586
[item_name] => IW***** Fee
[item_number] => g**5
[st] => Completed
[tx] => 9J5******81R
)
and an example of a failure case (no "cm")
(
[feepaid] => Y
[PayerID] => 9J*******YG
[st] => Completed
[tx] => 3D2*****2457
[cc] => USD
[amt] => 75.00
)
The failure has "PayerID" which does not appear as a valid variable in PDT or IPN spec. Success case has no PayerID but has item_name and item_number (which are fixed in the button definition on PayPal merchant account).
Same here! Paypal no longer return cm paramater for some users
Even checkout process is completed using Paypal Sandbox, order status shows Payment Pending.
My configuration is wrong?
I had the same issues, could be lots of things but I wasn't aware that in case of physical products woocommerce doesn't complete the order upon payment, that's because you should pack and send to the customer still so required some action from the seller.
in class-wc-order.php core woocommerce you can find this function
public function payment_complete( $transaction_id = '' ) {
...
$this->set_status( apply_filters( 'woocommerce_payment_complete_order_status', $this->needs_processing() ? 'processing' : 'completed', $this->get_id(), $this ) );
...
}
the needs_processing() will return true if the product is not virtual.
I changed this behaviour with a simple hook on filter woocommerce_order_item_needs_processing that you can find inside the function needs_processing()
/**
* this hooks turn status complete for every order upon payment.
*/
add_filter('woocommerce_order_item_needs_processing','my_filter');
//see class-wc-order line 1368 to understand the return value
function my_filter(){
return false;
}
but really, this issue can be lots of thing so this is not your case then have a look to the woocommerce pay pal doc here https://docs.woocommerce.com/document/paypal-standard/#section-21
I'm building a book store and I am building the checkout using PayPal Payflow . This is the checkout flow:
Shipping info --> Billing info |verify CC using Paypal| --> Order summary --> Submit |authorize CC using Paypal|
Shipping info: fill out shipping address, nothing special here
Billing info: fill out your billing address + credit card info. Don't save the credit card info since it's against standards, instead just send the CC number, expiration date, and CVV directly to PayPal to verify. PayPal approves.
Order summary: The order sees the summary of his order before he submits the order. He presses submit and another request to PayPal is sent to authorize the funds.
However, the CC info vanishes after #2, so how would I persist that data to #3 so that I can send it to PayPal again?
Can I just use the ORIGID to point to the PNREF ? The documentation says I have to do a full request with the whole params list (including CC info, CVV, exp date, etc).
TRXTYPE=A&TENDER=C&PWD=x1y2z3&PARTNER=PayPal&VENDOR=SuperMerchant&USER=S
uperMerchant&ACCT=5555555555554444&EXPDATE=0308&AMT=123.00&COMMENT1=Seco
nd purchase&COMMENT2=Low risk customer&INVNUM=123456789&STREET=5199
MAPLE&ZIP=94588
Or am I just misunderstanding what authorization means? Isn't authorization actually reserving funds in the user's CC? So that shouldn't be done until the user presses submit order right?
I figured it out.
The documentation here: https://www.paypalobjects.com/webstatic/en_US/developer/docs/pdf/pp_payflowpro_guide.pdf
on page 40 mentions it briefly, but doesn't go into much detail about this checkout flow even though it seems pretty common.
My assumption was right, in that I could just do an address verification request first with all the CC info, and use the PNREF returned. I save the PNREF id in my session and reuse it to submit a request that looks like this:
def authorize_transaction(pnref)
make_request(authorization_data(pnref))
end
def authorization_data(pnref)
{
"TRXTYPE" => "A",
"TENDER" => "C",
"USER" => PAYPAL_API["user"],
"PWD" => PAYPAL_API["pwd"],
"VENDOR" => PAYPAL_API["user"],
"PARTNER" => "Paypal",
"AMT" => purchase.total_price,
"ORIGID" => pnref,
"VERBOSITY" => "HIGH"
}
end
And receive the desired response:
{"RESULT"=>"0", "PNREF"=>"A10A6A9C08E1", "RESPMSG"=>"Approved", "AUTHCODE"=>"752PNI", "AVSADDR"=>"Y", "AVSZIP"=>"Y", "HOSTCODE"=>"A", "PROCAVS"=>"Y", "VISACARDLEVEL"=>"12", "TRANSTIME"=>"2014-01-31 11:53:56", "FIRSTNAME"=>"net", "LASTNAME"=>"theory", "AMT"=>"15.64", "ACCT"=>"1111", "EXPDATE"=>"0115", "CARDTYPE"=>"0", "IAVS"=>"N"}
I had implemented IPN handler (copt paste it from code project) in one of the project for the first time.
The problem in the user can registered to my website with some email address and then while paying he can use some different email address
And the IPN handler request variable give the email address with which it pays. How should i find out which user had paid.
if (strResponse == "VERIFIED")
{
//check the payment_status is Completed
//check that txn_id has not been previously processed
//check that receiver_email is your Primary PayPal email
//check that payment_amount/payment_currency are correct
//process payment
string payerEmail = Request.Form["payer_email"];
string paymentStatus = Request.Form["payment_status"];
string receiverEmail = Request.Form["receiver_email"];
string amount = Request.Form["mc_gross"];
}
Solutions:
Pass some addiditonal may be user id in the payment processing page and assume it will be returned in IPN handler.
Or ask the user to enter the paypal email address before paying. (not feels good)
Any help in this regard is appreciated
I usually POST, within the cart info, a custom field that identifies my local transaction, or my local cart. IPN will send that custom field back to you, so that you can match the transaction with your user. Or you can just pass the USER_ID, and get it back regardless of the transaction.
Send paypal your custom data in your form using this field:
<input type="hidden" name="custom" value="YOUR_CUSTOM_INFO_HERE">
Handle IPN in your callback:
if (strcmp ($res, "VERIFIED") == 0) {
// This is the custom field i posted within the cart form.
$cid = $_POST['custom'];
$txn_id = $_POST['txn_id'];
$cart = load_cart_by_cid($cid);
if (!empty($cart)) {
// Fetch your user from cart and do things with it,
// along with your security checks.
$user = $cart->getUser();
// For example, store the transaction.
TransactionManager::saveTransaction($user, $txn_id);
}
}
Source is php, but i think is verbose enough to be easily translated in java, or c#, or whatever.