need help with last mile on a custom gateway, putting 1 + 1 together...


I am building a custom gateway and I need your kind help, please.

Attached, is a file called: gateway.pesapal.php - it is 3kb, and we are 99% sure it is fine, we made a test that produced the forms we needed. It is built based on paypalsolo, but only needs 1/10th of what paypalsolo has.

Also attached, is a file called will_integrate_this_into_gatewaypesapal.php. It has the complete and exact code that the payment provider, called PesaPal, wants us to use, indicated in the exact steps. It is 2kb.

Now, we are totally lost on how to bring file 2 into file 1 so all is well.

We have been at it for 2 days and cant seem to get it right. Individually, they are perfect, but they need to marry and we dont know how to do that.

Is it possible for you to please look at the two files and give us a quick copy pasted file that merges the two in the right places? We can debug and clean up ourselves.

Thank you very much!


  • David

    Addon Name: PesaPal Single Payments Gateway
    Author: David Gikandi
    Author URI:
    Gateway ID: pesapal

    class pesapal extends Membership_Gateway {

    var $gateway = 'pesapal' ;
    var $title = 'PesaPal with Single Payments' ;
    var $issingle = true ;

    public function __construct() {

    add_action( 'M_gateways_settings_' . $this->gateway, array( &$this, 'mysettings' ) );

    // If I want to override the transactions output - then I can use this action
    //add_action('M_gateways_transactions_' . $this->gateway, array(&$this, 'mytransactions'));

    if ( $this->is_active() ) {
    // Subscription form gateway
    add_action( 'membership_purchase_button', array( &$this, 'display_subscribe_button' ), 1, 3 );

    // Payment return
    add_action( 'membership_handle_payment_return_' . $this->gateway, array( &$this, 'handle_pesapal_return' ) );
    add_filter( 'membership_subscription_form_subscription_process', array( &$this, 'signup_free_subscription' ), 10, 2 );

    function mysettings() {

    function single_sub_button($pricing, $subscription, $user_id, $norepeat = false) {

    global $M_options;
    $form = "
    <img src='' alt='Subscribe' title='Subscribe' style='width:128px' />

    return $form;

    function build_subscribe_button($subscription, $pricing, $user_id, $sublevel) {

    if(!empty($pricing)) {

    // check to make sure there is a price in the subscription
    // we don't want to display free ones for a payment system
    $free = true;
    foreach($pricing as $key => $price) {
    if(!empty($price['amount']) && $price['amount'] > 0 ) {
    $free = false;

    if(!$free) {
    if(count($pricing) == 1) {
    // A basic price or a single subscription
    if(in_array($pricing[0]['type'], array('indefinite','finite'))) {
    // one-off payment
    return $this->single_sub_button($pricing, $subscription, $user_id, false);
    } else {
    // simple subscription
    return $this->single_sub_button($pricing, $subscription, $user_id, true);
    } else {
    // something much more complex
    // Complex buttons currently not supported by PesaPal
    //return $this->complex_sub_button($pricing, $subscription, $user_id);




    function display_subscribe_button($subscription, $pricing, $user_id, $sublevel = 1) {

    if(isset($pricing[$sublevel - 1]) && $pricing[$sublevel - 1]['amount'] < 1)
    echo $this->single_free_button($pricing, $subscription, $user_id, $sublevel);
    echo $this->build_subscribe_button($subscription, $pricing, $user_id, $sublevel);


    function build_custom( $user_id, $sub_id, $amount, $sublevel = 0, $fromsub = 0 ) {
    global $M_options;

    $custom = time() . ':' . $user_id . ':' . $sub_id . ':';
    $key = md5('MEMBERSHIP' . $amount);

    if ( $fromsub === false ) {
    $fromsub = filter_input( INPUT_GET, 'from_subscription', FILTER_VALIDATE_INT );

    $custom .= $key;
    $custom .= ":" . $sublevel . ":" . $fromsub;

    return $custom;

    function update() {
    // default action is to return true
    return true;


    Membership_Gateway::register_gateway( 'pesapal', 'pesapal' );

  • David

    and here is the one we need to insert appropriately within the above file:


    // Step 1: Package the request to post to PesaPal and sign it
    // Include OAuth

    include_once ( 'OAuth.php' ) ;

    //Assign Variables

    $token = $params = NULL ;
    $consumer_key = 'XXXXXXXXXXXX' ;
    $consumer_secret = 'XXXXXXXXXXXX' ;
    $signature_method = new OAuthSignatureMethod_HMAC_SHA1( ) ;
    $iframelink = '' ;

    // Step 2: Post the request to PesaPal and load the PesaPal payments page
    // Assign Form Details passed to pesapal?iframe.php from shopping?cart?form.php to the specified variables.
    //get form details

    $amount = $_POST['amount'];
    $amount = number_format( $amount, 2 ) ;

    //format amount to 2 decimal places

    $desc = $_POST['description'];
    $type = $_POST['type']; //default value = MERCHANT
    $reference = $_POST['reference'];

    //unique order id of the transaction, generated by merchant

    $first_name = $_POST['first_name']; //[optional]
    $last_name = $_POST['last_name']; //[optional]
    $email = $_POST['email'];

    // Construct the post_xml variable. The format is standard so no editing is required. Encode the variable using htmlentities.

    $callback_url = '' ; //redirect url, the page that will handle the
    response from pesapal.
    $post_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><PesapalDirectOrderInfo xmlns:xsi=\"\" xmlns:xsd=\"\" Amount=\"" . $amount . "\" Description=\"" . $desc . "\" Type=\"" . $type . "\" Reference=\"" . $reference . "\" FirstName=\"" . $first_name . "\" LastName=\"" . $last_name . "\" Email=\"" . $email . "\" PhoneNumber=\"" . $phonenumber . "\" xmlns=\"\" />" ;
    $post_xml = htmlentities( $post_xml ) ;

    // Construct the OAuth Request url. Using the Oauth class included construct the oauth request url using the parameters declared above (the format is standard so no editing is required)

    $consumer = new OAuthConsumer( $consumer_key, $consumer_secret ) ;

    //post transaction to pesapal

    $iframe_src = OAuthRequest :: from_consumer_and_token( $consumer, $token, "GET", $iframelink, $params ) ;
    $iframe_src->set_parameter( "oauth_callback", $callback_url ) ;
    $iframe_src->set_parameter( "pesapal_request_data", $post_xml ) ;
    $iframe_src->sign_request( $signature_method, $consumer, $token ) ;

    // Step 3: Display a post-payment page to your customer
    //display pesapal - iframe and pass iframe_src

    < iframe src = "<?php echo $iframe_src;?>" width = "100%" height = "700px" scrolling = "no" frameBorder = "0" > < p > Browser unable to load iFrame < / p > < / iframe >

    // Step 4: Query PesaPal for payment status


  • David

    Hi Ash,

    Thanks for the tip on pastebin, had no idea.

    Simply pasting will not work. I tried it already.

    The problem is, Membership doesn't give good guidance on how we can build custom gateways.

    For example, we are unclear as to how to save data (do we use SQL? Which tables do we save what info?...). Another area of challenge is, what function does what?

    So what we did, in the first file, we took out everything we felt we did not need from paypalsolo. In the second file, we took the sample code given by our payment gateway called pesapal, which is arranged in a step by step fashion as per the steps they require.

    Problem is, the first file (the stripped down paypalsolo edit) is object oriented. The second file is a step by step flow. We need to take that step by step flow and merge it into the functions in the first file, appropriately. An easy task for someone who is good at php and understands the gateways structure, but without that understanding, its extremely confusing.

    So a simple paste of file 2 to the bottom of file 1 won work because it breaks the functions/flow structure.

    That is where we needed help in. Are you able to advice further?



  • Rahul Verma

    Hello David,

    After looking at your code, It still little bit far behind to combine it and use it as a new gateway for membership plugin. It should be around 6-7 hrs of work to make it working, test with both single and multi-site setup so might be you can consider hiring a developer from to develop it for you.

    I'm willing to help you more if you have more question on the same.

    Best Regards
    Sandeep Kumar

  • David

    Hi @Ashok and @Sandeep Kumar,

    I'm trying to implement a custom membership, where my the payment gateway i'm using takes about a day to process payment, then sends a callback to my server (ipn.php) with a success/ failure flag and a payment id and user id of the person who made the payment.

    If the payment is successful, is this the code I need to use in ipn.php to register the user? If so:
    1. what specific includes should I add to the top of ipn.php
    2. what happens if($member) resolves to false?

    $member = Membership_Plugin::factory()->get_member($user_id);
    if($member) {
    if($member->has_subscription() && $member->on_sub($sub_id)) {
    remove_action( 'membership_expire_subscription', 'membership_record_user_expire', 10, 2 );
    remove_action( 'membership_add_subscription', 'membership_record_user_subscribe', 10, 4 );
    $member->create_subscription($sub_id, $this->gateway);
    } else {
    $member->create_subscription($sub_id, $this->gateway);
    $this->record_transaction($user_id, $sub_id, $amount, $M_options['paymentcurrency'], time(), ( $payment->results[6] == 0 ? 'TESTMODE' : $payment->results[6]) , $status, $note);

    Thanks and cheers!


  • Hoang Ngo

    Hi David.

    I hope you are well today.

    what specific includes should I add to the top of ipn.php

    So you mean to subscribe user to a plan ?. If so, here is the code for it

    if ($sub_id && $user_id && $_REQUEST['key'] == $hash && $_REQUEST['credit_card_processed'] == 'Y') {
    				$this->_record_transaction($user_id, $sub_id, $_REQUEST['total'], $_REQUEST['currency'], $timestamp, $_REQUEST['order_number'], 'Processed', '');
    				// Added for affiliate system link
    				do_action('membership_payment_processed', $user_id, $sub_id, $_REQUEST['total'], $_REQUEST['currency'], $_REQUEST['order_number']);
    				$member = Membership_Plugin::factory()->get_member($user_id);
    				if($member) {
    					$member->create_subscription($sub_id, $this->gateway);
    					membership_debug_log( sprintf(__('Order complete for user %d on subscription %d.', 'membership'), $user_id, $sub_id ) );
    				do_action('membership_payment_subscr_signup', $user_id, $sub_id);

    The condition is check does the IPN return is valid, this is for 2Checkout gateway, and this will different from your gateway, I believe their document will have this section.

    Basically, the flow for signup new user to a plan is
    Check if the return is valid -> Record Transaction -> Get member through membership model -> create subscription.

    2. what happens if($member) resolves to false?

    If the $member is fall, so no membership will created for that member. The most case it's return false is the member_id not exist, it's mean some hacking are going on your server.

    If you have any issues please don't hesitate to let us know so we can assist

    Best regards,

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.