In a recent project I developed a module that allows for setting up PayPal chained payments as a payment gateway in Drupal Commerce. I used rszrama's commerce_paypal_wps as my starting point (major props to Ryan - awesome code to work from!) and leveraged as much from the parent commerce_paypal module as I could.

I've tested the module pretty extensively with sandbox credentials and it appears to be working well.

In its current form, it could be offered up to the community as a submodule in this project, or a stand-alone module with a commerce_paypal dependency. Looking for rszrama's feedback on which route to go there.

PayPal's chained payments uses the Adaptive Payments API, which differs pretty vastly in terms of how variables get sent to PayPal, what those variables are, and what variables come back to the site via the IPN. Because of this I had to abandon certain parts of commerce_paypal's standard IPN processing.

In a general sense, my module reaches out to PayPal with a definition of the paychain, receives a paykey, then drops the buyer on PayPal's site, with the paykey, to complete the transaction. The module then harvests the IPN from PayPal and if everything checks out, creates the payment transactions on the order, bringing the order to a fully-paid state, with a new field on the Payments tab showing who was paid what (shows the "receiver email address" for each payment in the chain).

I also designed the module in such a way that you can define the paychain using tokens (assuming you have token installed) - so who gets paid what percentage of the order could be dynamic and specific to each order.

Currently, this module is sitting in my GitHub account here:
https://github.com/chrisolof/commerce_paypal_chained

If it's possible this could become a submodule in this project (like commerce_paypal_wps), I could submit a patch against 7.x-2.x-dev - just let me know. For now, I'll keep working on it in GitHub.

Caveats:
- Currently the module does not handle defining time-delays in the pay chain (might be easy to add), nor does it handle chained refunds (could be complex to add).
- The module requires the web server have cURL support in PHP (reaching out for the paykey is done through cURL).
- The module could use better watchdog/error reporting in instances where a paykey does not come back from paypal.
- The module could use additional validation surrounding the paychain definition so we never reach out to PayPal with an invalid paychain.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mjgruta’s picture

Hi Cris,

I've tested your module using sandbox account and it is all working well.

but in my use case...

The receivers email will be coming from a field that I created on the user seller account.
So each seller can create a product and sell it.
Buyers can add any products to their cart but limited to 4 seller only.

I'm thinking of creating a logic that will group the items based on the seller and calculate the total percentage for each seller, then altering your configuration when the buyer checkout.

Can it be done? is there any recommendation that you can give to me?

Thanks!

alex_qwe’s picture

Hello,
Great work, everything works. I have the same issue as jhayzhon.
Its great that you can use tokens on field, unfortunately is not much use for our case, as there is no access to product tokens from order line items.
Is it difficult to set selector input for emails? Just like orders? This way it will be a lot more customizable with rules.

Thanks again.

And it definitely shoudl be included as submodule for commerce paypal.

alex_qwe’s picture

Ok, I solved this problem but with a very nasty solution.
In the rules payment module add a parameter for seller email.
Then in enable method function save it in a session variable (couldn't figure out how to get this parameter from within payment chained module :( , if you know how, please let me know)

alamp’s picture

This is a tip for a novice as like me.

If you face errors as running PayPal Chained, please add debugging message as follows:

--- a/commerce_paypal_chained.module
+++ b/commerce_paypal_chained.module
@@ -1063,6 +1063,7 @@ function commerce_paypal_chained_get_paykey($receivers, $memo = '', $tracking_id
   curl_close($ch);
   // Our response was in JSON, so decode it into a PHP object
   $result = json_decode($result);
+  dd($result,date("h:i:s" ,time()));
   // If PayPal gave us an acknowledgement of success and a paykey, proceed
   if (strtolower($result->responseEnvelope->ack) == 'success' && !empty($result->payKey)) {
     // Return the PayKey

dd will print the output of the response from Paypal, which includes error codes as follows:

 [responseEnvelope] => stdClass Object
         (
             [timestamp] => 2014-01-28T18:09:08.174-08:00
             [ack] => Failure
             [correlationId] => 930c6def0ef5d
             [build] => 7935900
         )
 
     [error] => Array
         (
             [0] => stdClass Object
                 (
                     [errorId] => 560022
                     [domain] => PLATFORM
                     [subdomain] => Application
                     [severity] => Error
                     [category] => Application
                     [message] => The X-PAYPAL-APPLICATION-ID header contains an invalid value
                     [parameter] => Array
                         (
                             [0] => X-PAYPAL-APPLICATION-ID
                         )
 
                 ) 
         )
alamp’s picture

I slightly changed Commerce PayPal Chained for Adaptive Simple Payments.

  1. Remove receivers from the settings, Instead module will find the Paypal email account of product owner
  2. User can fill in the paypal account form when they sign up.

With Paypal sandbox, it works, but I didn't check out with IPN since I'm ignorant of IPN.

The source is from:
https://github.com/PlusGenie/commerce_paypal_simple

shabirahmad’s picture

Hello Guys,

It is very good effort, I like the module but I have one question. I want to populate the receiver emails based on my products and the shop owners of the products. Right now I have created a rule that is fired whenever a new order is submitted. In that rule I have written a php snippet that calculates distinct receiver emails and their percentage price values, Now here comes the problem. How can I update the payment method configuration form to put these data??

Any idea would be highly appreciated!!

This is the function that I use to get the receiver emails and their percentages

function ti_common_adaptive_payment_rule($commerce_order){

$order_wrapper = entity_metadata_wrapper('commerce_order', $commerce_order);
$total_price = $order_wrapper->commerce_order_total->value()['amount'];
//dpm($total_price);
//$total_price =;
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
$line_item = $line_item_wrapper->value();
$line_item_price = $line_item->commerce_total[LANGUAGE_NONE][0]['amount'];
$product_id = (int)$line_item->commerce_product[LANGUAGE_NONE][0]['product_id'];
$line_item_percentage[$delta]['price'] = ($line_item_price*100)/$total_price;
$product_wrapper = entity_metadata_wrapper('commerce_product', $product_id);
$store_id = $product_wrapper->cmp_store->value()->id;

$store_wrapper = entity_metadata_wrapper('commerce_store', $store_id);
$marchent_email = $store_wrapper->field_store_merchant_email->value();
$line_item_percentage[$delta]['reciever_email'] = $marchent_email;
// dpm($line_item_price);
// dpm($line_item);
}
$in = $line_item_percentage; // your input
$out = array();
foreach ($in as $row) {
if (!isset($out[$row['reciever_email']])) {
$out[$row['reciever_email']] = array(
'reciever_email' => $row['reciever_email'],
'price' => 0,
);
}
$out[$row['reciever_email']]['price'] += $row['price'];
}
$out = array_values($out); // make the out array numerically indexed

$query = db_query('select * from {commerce_paypal_chained_pay_chain}');
foreach ($query as $value) {
# code...
$unserialize = unserialize($value->data);

foreach ($out as $key => $value) {
$unserialize[$key+1]['amount'] = $value['price'];
$unserialize[$key+1]['email'] = $value['reciever_email'];
$unserialize[$key+1]['primary'] = false;
}
$serialize_data = serialize($unserialize);
$query = db_query('UPDATE {commerce_paypal_chained_pay_chain} SET data = :data',array(':data' => $serialize_data));
//dpm($unserialize);
}

}

shabirahmad’s picture

Never mind, I have figured it out. I have changed the logic of commerce_paypal_chained_order_form this function and put in the receivers array my calculated ones, It is now working perfectly!!

alamp’s picture

@shabirahmad

>commerce_paypal_chained_order_form this function and put in the receivers array my calculated ones
>
could you post it please? Thanks in advance.

rbeaujard’s picture

Hey guys, I'm working on getting this module to work in my scenario. I need one modification that I hope someone can help with. I need to subtract the shipping amount from the amount split between the secondary receivers. In my case, the primary payment receiver will also be the shipper and hence the shipping fees should be passed to them then the split for payouts should occur based on the remainder.

I'm very much a newbie with Drupal programming (barely out of site builder status) but it seems that this could easily be done in commerce_paypal_chained.module at line 829

$calculated_amount = ($percentage / 100.00) * $amount;

If I could get this to be something like

$calculated_amount = ($percentage / 100.00) * ($amount-$shipping_amount);

that looks to serve my purposes. I'm wondering if any taxes should also be treated like this as they will most likely be absorbed by the primary receiver. These both seem to be easy enough to pop a toggle switch in the settings. I'm not sure how to access the shipping variables of the order. If anyone could help it would be greatly appreciated. As I said, I'm a serious newbie with Drupal module modification but I will try to figure this out until someone chimes in. Thanks in advance for anyone able to help out.

RJB

chrisolof’s picture

So cool to hear this is getting used by all of you!

Since I haven't heard anything from the commerce module maintainer regarding the possibility of getting this included into Commerce PayPal, I'm going to attempt to release it as its own stand-alone module when I get the time (hopefully soon!). That way it'll get it's own issue queue (allowing for the community to improve it) and be more findable by those of us who need this chained payment functionality in our commerce projects.

Also - since I keep getting asked a similar question about how to dynamically create the payment chain - the best answer I have (and the one I used in my project) was to make sure to install the Token module and use tokens off of the order object as placeholders in the payment chain configuration. With entity reference fields on the order you could potentially store references to the users in the pay chain as the order is created. Then, in the Chained Payment configuration you could use the Token module's placeholders to reference the user email addresses for the users stored in the payment chain on the order. Anyway - with token and entity (user) reference fields the process should be pretty straightforward. I'll give a better example in the README.txt once this module is released.

bsandor’s picture

Hi Guys,

Just wondering if commerce_paypal_chained_ipn_delete() is ever called .

I can't see the overall flow of this module so might be wrong of course.

clojurmcmodder’s picture

Hi Chris, I keep on getting a empty key when the module tries to contact Paypal for the key. This causes the json decode function to return a null object, eventually causing a unable to get empty object error. Please help.

clojurmcmodder’s picture

Lorem ipsum

clojurmcmodder’s picture

Just wondering, when is this going to be turned into a separate module?

bsandor’s picture

1./ I'm also wondering why we use commerce_paypal_chained_process_ipn() instead of using the original commerce_paypal_process_ipn().

2./ If commerce_paypal_chained_process_ipn() stays than I think it's last line should be:

module_invoke_all('commerce_paypal_chained_ipn_process', $order, $payment_method, $ipn);

I haven't checked the code of the two functions also they look very similar.

nvahalik’s picture

Title: Commerce PayPal Chained » Support PayPal Adaptive Payments (Chained Payments)
FileSize
24.44 KB

I know there is a module that was already written but after talking with @rszrama, I'm rewriting the existing work (hat-tip to the OP, @chrisolof, for his fine work which helped me greatly). It differs in that it is a lot more minimal and removes many of the features ("assumptions") that exist in the Github version of the module.

Current features:

  • Supports building and sending PayRequests
  • Adds API for other modules to modify the payment request before being sent.
  • Utilizes the PayPal module's IPN notifier system.
  • Plenty of developer documentation.
  • Debugging and logging support.

Initial patch for feedback. Stuff that still probably ought to be done:

  • Support for PaymentDetails request.
  • UI cleanup.
  • Consideration and implementation of IPN handler hook for this module's API.

There are many things that are not supported at this time like Preauthorizations and others, but this at least provides a basis for future work.

mglaman’s picture

Snarky review incoming

  1. +++ b/modules/adaptive/commerce_paypal_adaptive.install
    @@ -0,0 +1,2 @@
    +<?php
    +
    

    Wha?

  2. +++ b/modules/adaptive/commerce_paypal_adaptive.module
    @@ -0,0 +1,468 @@
    +  return json_decode($result);
    

    Use drupal_json_decode

nvahalik’s picture

Fixed some C&Ps.
Fixed a misspelled function name.
Other code review cleanup.

joshmiller’s picture

Status: Active » Needs work
  1. +++ b/commerce_paypal.module
    @@ -472,6 +472,8 @@ function commerce_paypal_currencies($method_id) {
    +      return drupal_map_assoc(array('AUD', 'BRL', 'CAD', 'CZK', 'DKK', 'EUR', 'HKD', 'HUF', 'ILS', 'JPY', 'MYR', 'MXN', 'NOK', 'NZD', 'PHP', 'PLN', 'GBP', 'SGD', 'SEK', 'CHF', 'TWD', 'THB', 'TRY', 'USD'));
    

    Could share the same currencies with paypal_wps case (kudos to Ryan seeing this)

  2. +++ b/modules/adaptive/commerce_paypal_adaptive.module
    @@ -0,0 +1,468 @@
    +  // some reason, instead of getting and processing Express Checkout payment
    

    Probably want to remove express checkout from these comments.

  3. +++ b/modules/adaptive/commerce_paypal_adaptive.module
    @@ -0,0 +1,468 @@
    + * Builds a Website Payments Standard form from an order object.
    

    WPS mention fail

  4. +++ b/modules/adaptive/commerce_paypal_adaptive.module
    @@ -0,0 +1,468 @@
    +  if (!empty($settings['allow_supported_currencies']) && in_array($order_currency_code, array_keys(commerce_paypal_currencies('paypal_wps')))) {
    

    Want to use commerce_paypal_currencies('paypal_apc') ?

  5. +++ b/modules/adaptive/commerce_paypal_adaptive.module
    @@ -0,0 +1,468 @@
    + * Returns the URL to the specified PayPal WPS server.
    

    WPS fail

Didn't test it ... just a few catches above. Also documented Ryan's feedback from the internal CG Hipchat (not sure if he will post a more thorough review).

nvahalik’s picture

Status: Needs work » Needs review

All of the above issues were addressed in the patch above.

nvahalik’s picture

drupal_json_decode() is incompatible with json_decode() so more work had to be done to convert over.
Added $settings array to data alter.

nvahalik’s picture

Added PaymentDetails support.
Updating the transaction status when coming back from the payment page.
Stubbing in IPN handling.

nvahalik’s picture

Removed a dpm().
Added a menu alter hook to override the IPN callback. Tagged an issue that might not actually be an issue. Essentially, the format for Adaptive Payments IPN breaks the IPN handling that Commerce PayPal does and some cleanup must be done in order to ensure that the IPN validates with PayPal successfully. That ticket is open in case it makes sense for us to fix it in the main module instead of having us fix it here in the submodule.

nvahalik’s picture

Added hook_commerce_paypal_adaptive_secondary_transaction() to allow modules to map secondary receivers in the primary IPN to transactions.
Implemented the primary IPN processing function which handles the primary transaction in a separate function and then cycles through the secondary transactions to update their status. Supporting Canceled, Completed, and Refunded secondary transactions.
Basic support and logging for primary receiver transactions has been added.
Fixed a problem where the transaction failing to create properly wouldn't properly create a failed transaction on the order.
Reworked the commerce_paypal_adaptive_ipn_override() so that you could actually debug with it.
Changed some of the docs so that it more properly conveys why the code does what it does (and doesn't throw stones).
Broke out the functions that are used to fix the request so that they could be tested better.

vignesh226’s picture

Hi can you give workflow for this?

keith.penn27’s picture

Hello everyone, I am trying to use this module in my commerce marketplace setting on D7 but I keep getting an empty pay key error back from paypal. I have my sandbox API credentials set up correctly so I'm not sure where to go from here. I am using the module from the github page and have added any patches yet. Can anyone help me with getting this working? Thanks in advance!

nvahalik’s picture

This updated patch:

  • Fixes some PAReview issues
  • Adds a note to the README with information about implementing hook_commerce_paypal_apc_order_form_data_alter() which is required to add additional payment recipients

There isn't a lot of additional information but you should look at implementing the above hook and read the docs included in the API.

keith.penn27’s picture

Thank you for the info! I will try this out soon and take a look at hooks.

keith.penn27’s picture

Hello again! I have installed your module and implemented hook_commerce_paypal_apc_order_form_data_alter() to add secondary receivers. The receiver's email is being pulled from a custom field on the store entity type. But I'm still having some issues. API credentials have been added to the supplied rule in adaptive payments. Trying to complete orders does not direct me to Paypal to complete the payment. Any advice on this? I'm hosting locally right now for development, otherwise I would send you a link.

nvahalik’s picture

@keith.penn27, It's been a while since I originally implemented this. As far as errors from PayPal go, if you want to paste them here it might be possible to address them this way, but it without a working environment I'm afraid my ability to support this via the issue queue is a bit restricted.

If you are not getting redirected to PayPal that sounds like either there is an issue with your configuration or the payment gateway just plain isn't being utilized. However you'd need to provide more information than what is currently here.

keith.penn27’s picture

I can export and post my current db configuration later today if that would help along with the custom mod implementing the hooks.

nvahalik’s picture

Unfortunately, I'm not really in a place to provide support above and beyond the code and pointers that were updated yesterday right now.

Post the error messages and see if anyone else has run into it before. If you are interested in paid support, you can contact me through my personal contact page on d.o. Otherwise I can come back and revisit the issue as my time allows.

gopikakrish84’s picture

Hi nVahalik,

I had actually installed this module and it was working well with sandbox. I had to make it live, so I created a live app and gave live api information also. But somehow now I always get an error stating Invalid transacation. Please contact merchant. I am not sure where I have to check. It would be great if you can give some pointers.

Regards, Gopika

nvahalik’s picture

gopikakrish84, As I mentioned above I am not really in a position to provide free support to you, especially if you are getting errors from PayPal. You will have to enable debugging and figure it out yourself.