Cancelling Subscriptions Created with PayPal Standard via the Express Checkout API

PayPal makes it possible to accept payments via just a few pieces of HTML on your site with its Website Payments Standard service. This service includes a Subscriptions button to sell products or services with recurring payments.

The trouble is, PayPal doesn’t make it as easy to cancel a subscription on your site. The documentation on cancelling a subscription only provides instructions for manually cancelling a subscription from the PayPal administration interface.

The good news is, there is another, undocumented method to cancel subscriptions on your site.

Enter Express Checkout

Although PayPal Standard has no API for cancelling a subscription, PayPal offers another product which has a more comprehensive API – Express Checkout.

The gotcha here is that although Website Payments Standard and Express Checkout have distinct branding and documentation, some PayPal Express Checkout API operations can actually be performed with the values of a PayPal Standard transaction.

The API operation for cancelling subscriptions is ManageRecurringPaymentsProfileStatus.

ManageRecurringPaymentsProfileStatus

When a customer creates a subscription with PayPal Standard, PayPal notifies your site with an IPN request. The request contains a set of name values pairs which will look something like this:

[txn_type]          => subscr_signup
[subscr_id]         => I-NARPL1C00000
[last_name]         => User
[residence_country] => US
[mc_currency]       => USD
[item_name]         => Digital Subscription
[business]          => seller@example.com
[recurring]         => 1
[verify_sign]       => AFcWxV21C7fd0v3bYYYRCpSSRl31AEFeAejFlhh0AvqwNRts6zLECRVi
[payer_status]      => verified
[test_ipn]          => 1
[payer_email]       => buyer@example.com
[first_name]        => Test
[receiver_email]    => seller@example.com
[payer_id]          => K48P3FBQAAAAA
[invoice]           => your_invoice_id
[reattempt]         => 1
[recur_times]       => 4
[subscr_date]       => 18:13:30 Apr 17, 2012 PDT
[custom]            => 25
[charset]           => windows-1252
[notify_version]    => 3.4
[period1]           => 1 D
[mc_amount1]        => 11.00
[period3]           => 1 D
[mc_amount3]        => 5.50
[ipn_track_id]      => baca1234

The most important part of this request is the subscr_id. This field is actually equivalent to a PayPal Express Checkout Recurring Payment Profile ID and can be passed as the PROFILEID of a ManageRecurringPaymentsProfileStatus NVP API operation.

Example

The full documentation on the ManageRecurringPaymentsProfileStatus NVP API operation is available here. Unfortunately it lacks an example.

Here’s an example function in PHP for calling the ManageRecurringPaymentsProfileStatus API operation:

/**
 * Performs an Express Checkout NVP API operation as passed in $action.
 *
 * Although the PayPal Standard API provides no facility for cancelling a subscription, the PayPal
 * Express Checkout  NVP API can be used.
 */
function change_subscription_status( $profile_id, $action ) {

	$api_request = 'USER=' . urlencode( 'api_username' )
				.  '&PWD=' . urlencode( 'api_password' )
				.  '&SIGNATURE=' . urlencode( 'api_signature' )
				.  '&VERSION=76.0'
				.  '&METHOD=ManageRecurringPaymentsProfileStatus'
				.  '&PROFILEID=' . urlencode( $profile_id )
				.  '&ACTION=' . urlencode( $action )
				.  '&NOTE=' . urlencode( 'Profile cancelled at store' );

	$ch = curl_init();
	curl_setopt( $ch, CURLOPT_URL, 'https://api-3t.sandbox.paypal.com/nvp' ); // For live transactions, change to 'https://api-3t.paypal.com/nvp'
	curl_setopt( $ch, CURLOPT_VERBOSE, 1 );

	// Uncomment these to turn off server and peer verification
	// curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
	// curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
	curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
	curl_setopt( $ch, CURLOPT_POST, 1 );

	// Set the API parameters for this transaction
	curl_setopt( $ch, CURLOPT_POSTFIELDS, $api_request );

	// Request response from PayPal
	$response = curl_exec( $ch );

	// If no response was received from PayPal there is no point parsing the response
	if( ! $response )
		die( 'Calling PayPal to change_subscription_status failed: ' . curl_error( $ch ) . '(' . curl_errno( $ch ) . ')' );

	curl_close( $ch );

	// An associative array is more usable than a parameter string
	parse_str( $response, $parsed_response );

	return $parsed_response;
}

Usage

To use this function, you will need to replace 'api_username', 'api_password' and 'api_signature' with your PayPal API credentials.

To cancel the subscription responsible for the IPN request above, we just pass in the value from the subscr_id field in the IPN request.

change_subscription_status( 'I-NARPL1C00000', 'Cancel' );

We can also use it to suspend a profile:

change_subscription_status( 'I-NARPL1C00000', 'Suspend' );

Or reactivate a suspended profile:

change_subscription_status( 'I-NARPL1C00000', 'Reactivate' );

Limitations

There are still a few limitations for which I haven’t been able to find a work around.

Shooting in the Dark

To suspend a subscription, it must be active. To reactivate a subscription, it must be suspended. Express Checkout provides an API operation to find the current state of a subscription – GetRecurringPaymentsProfileDetails (documented here).

Unfortunately, PayPal doesn’t allow this operation to use a subscr_id value as a PROFILEID. As a result, you’ll need to just shoot blind when managing a subscription and deal with any failures.

For reference, the parsed response of a failed ManageRecurringPaymentsProfileStatus request will look something like this:

[TIMESTAMP]       => 2012-06-29T06:54:22Z
[CORRELATIONID]   => ebe0a10c134
[ACK]             => Failure
[VERSION]         => 76.0
[BUILD]           => 3067390
[L_ERRORCODE0]    => 11556
[L_SHORTMESSAGE0] => Invalid profile status for cancel action; profile should be active or suspended
[L_LONGMESSAGE0]  => Invalid profile status for cancel action; profile should be active or suspended
[L_SEVERITYCODE0] => Error

No IPN

Compounding the problem above, PayPal sends no IPN message when a profile is suspended. If a user suspends a profile with PayPal directly, the profile will fall out of sync with your site.

You should also record the subscription status as suspended when making a successful ManageRecurringPaymentsProfileStatus request, instead of waiting for an IPN request.

An Easier Way to Sell Subscriptions

If you want to sell subscriptions with PayPal, but you don’t know a server-side programming language, or you just value your sanity enough to avoid the PayPal API, then checkout my WooCommerce Subscriptions extension.

Combined with the featured-packed WooCommerce and ease-of-use of WordPress, Subscriptions makes it easy to sell products & services with recurring payments. And it uses the method described here, so even subscriptions created with PayPal Standard can be cancelled, suspended and reactivated directly from your store.

UPDATE 11  Feburary 2013

There is one caveat – the subscription must have been purchased after 2009 for this method to work.

About Brent

Born to code.
This entry was posted in Blogdex, Hacker Tales and tagged , , , , . Bookmark the permalink.

37 Responses to Cancelling Subscriptions Created with PayPal Standard via the Express Checkout API

  1. tono says:

    wow.. the best explanation ive found so far to manage paypal subcription. Do you have any article how to create the subscription? Read your other article, but it seems you use URL parameter? I usually use form _xclick-subscription! And bit confuse about subscr_payment and recurring_payment. if you care to explain include tutorial would be great :D

    • Brent says:

      Hi Tono, take a look at the HTML Variables for Subscriptions section.

      These are all the options you can send along with _xclick-subscriptions. They can be sent either through a HTML form, e.g add a one month free trial period:

      <input type="hidden" id="cmd" name="cmd" value="_xclick-subscriptions" />
      <input type="hidden" id="a1" name="a1" value="0" />
      <input type="hidden" id="p1" name="p1" value="1" />
      <input type="hidden" id="t1" name="t1" value="M" />
       ... rest of your subscription's details
      

      Or as GET parameters in a URL string, e.g. add a one month free trial period:

      cmd="_xclick-subscriptions"&a1=0&p1=1&t1=M&... rest of your subscription's details
      
      • tono says:

        Hi Brent, thanks it helps a lot..
        Do you know the difference ‘subscr_payment’ and ‘recurring_payment’?
        And Do you know how to set Initial payment for subscription? at the moment i use trial fee as initial payment. Dont know wether its the right option to do..

        • Brent says:

          I think subscr_payment payment is for subscriptions created with PayPal Standard while recurring_payment is for those created with Express Checkout.

          Regarding initial payments. There is no option to create an initial payment for subscriptions created in Website Payments Standard, so I’ve done the same thing you do (which is use the trial period to add it to the first payment only).

  2. Good post. What I do not understand why my all subscription payments id start with S- and not with I-. When I post for example PROFILEID=S-7J596251UT364050T I get the error “The profile ID is invalid”.

    • Brent says:

      A payment transaction ID is different to a subscription’s Profile ID. Make sure you are using the Profile ID for the subscription and not the transaction ID for a payment which forms part of that subscription.

  3. I’m getting this response from PayPal. Guess they’ve shut down the ManageRecurringPaymentsProfileStatus method for express checkout? Any help would be greatly appreciated…

    ACK: Failure
    L_ERRORCODE0: 81002
    L_SHORTMESSAGE0: Unspecified Method
    L_LONGMESSAGE0: Method Specified is not Supported
    L_SEVERITYCODE0: Error

    • Brent says:

      Hi Alexander, I just tested it again and it’s still working for me. All I can suggest is to make sure each of your NVP variables & values are correct – PayPal has been known to provide an error message that has nothing to do with the real problem.

  4. Mohan says:

    Hi Brent,

    I have used your code to cancel the subscription using its profile ID like I-GY3M2WDV****. But, I’m getting the following Error from Paypal Sandbox.
    [TIMESTAMP] => 2012-12-05T05:04:33Z
    [CORRELATIONID] => b2d9399f85ff6
    [ACK] => Failure
    [VERSION] => 76.0
    [BUILD] => 4181146
    [L_ERRORCODE0] => 11552
    [L_SHORTMESSAGE0] => Invalid profile ID
    [L_LONGMESSAGE0] => The profile ID is invalid
    [L_SEVERITYCODE0] => Error
    I’m sure, I have that profile Id in my paypal sandbox. Even though, its returning error as such. Could you please tell me about the problem with me??

    Thanks

    • Brent says:

      Hi Mohan, all I can do is suggest checking the request and making sure the profile ID is being sent as you see it (i.e. not have any URL encoding/HTML escaping issues). If that fails, try creating a new profile and testing with that ID. I have since learned that only profiles created after 2009 will work via this method, so that might be the problem?

  5. Pingback: [PHP]Paypal APIで月額課金を止める方法と不正利用されない方法 | 田舎暮らしフリーランスへの道

  6. Bogdan says:

    Great! This is very well explained and most important it has a code example.
    Thank you very much!

  7. Pranav says:

    Hi
    I have created a recurring profile using subscribe button of Paypal on sandbox. But when i try to cancel it using the following code

    $api_request = ‘USER=’ . urlencode( ‘api_username’ )
    . ‘&PWD=’ . urlencode( ‘api_password’ )
    . ‘&SIGNATURE=’ . urlencode( ‘api_signature’ )
    . ‘&VERSION=76.0′
    . ‘&METHOD=ManageRecurringPaymentsProfileStatus’
    . ‘&PROFILEID=’ . urlencode( $profile_id )
    . ‘&ACTION=’ . urlencode( $action )
    . ‘&NOTE=’ . urlencode( ‘Profile cancelled at store’ );
    it gives me the following error
    Array
    (
    [TIMESTAMP] => 2013-02-08T10:19:58Z
    [CORRELATIONID] => 66adc7d019253
    [ACK] => Failure
    [VERSION] => 76.0
    [BUILD] => 5060305
    [L_ERRORCODE0] => 11552
    [L_SHORTMESSAGE0] => Invalid profile ID
    [L_LONGMESSAGE0] => The profile ID is invalid
    [L_SEVERITYCODE0] => Error
    )
    does “ManageRecurringPaymentsProfileStatus” this method not cancel the recurring created by standard paypal? if it cancels, then what to do? Please help.

    • Brent says:

      Based on the error message, I’d say check the Subscription ID/Profile ID you’re using in the request. Make sure it’s not the transaction ID.

  8. Pranav says:

    Hi Brent –

    Thanks for your response. I am using the following
    Profile start date Feb 7, 2013 | Profile ID No. I-XNEK7EVPSW4M
    I have copied it from my paypal account.

    Can we cancel the recurring created by PayPal standard by using ManageRecurringPaymentsProfileStatus
    ?

    Please confirm.

    Thanks

    • Brent says:

      Can we cancel the recurring created by PayPal standard by using ManageRecurringPaymentsProfileStatus

      Yes, you should be able too. That’s what the article is about.

  9. Pranav says:

    Hi
    Can it be done on sandbox?

  10. Pranav says:

    Hi Brent –

    We created the recurring using the following code .

    and the profile id that created was Profile ID No. I-XNEK7EVPSW4M.

    when i used the above mentioned code i.e. ManageRecurringPaymentsProfileStatus to remove the recurring, it did not work.

    Can you please tell me where I am wrong?

    Thanks

    • Brent says:

      I have no idea why it’s not working for you and you’ll need to figure that out for yourself now. PayPal is notoriously complicated, undocumented & inconsistent, so it could be how you’re creating the profile, the country your account is registered in or something completely different. I’d suggest contacting PayPal support and if you still can’t get it to work, use PayPal Express Checkout.

      • Pranav says:

        Hi Brent –

        Thanks for your help. When i created a new profile in sandbox paypal and used its credentials then it started to work. Thanks you for your support.

  11. Ben says:

    Thanks so much. Paypal should hire you to explain their messy system! I pity those who don’t find this explanation!

  12. Amarpreet SIngh says:

    Excellent Its work . I am implement this code in vb.net and its work properly.
    Thanks Brent

  13. David Rogers says:

    Fun fact: when using the “GetRecurringPaymentsProfileDetails” operation, you may receive this error: “Subscription Profiles not supported by Recurring Payment APIs”

    Apparently you can only get profile details for users created by the Classic API (“CreateRecurringPaymentsProfile” call). Another head scratcher from PayPal. Luckily this is the furthest I have to go down the rabbit hole as I have IPN data for the client (thankfully!) and can use “ManageRecurringPaymentsProfileStatus” to suspend profiles while I move the users to Stripe subscriptions (which I love). The old “S-” style profile IDs (pre-2009), as you mentioned, won’t work so I have to create a ticket for our support staff to remove them manually; worth it to get rid of PayPal.

    • David Rogers says:

      Oh by the way, Brent, thank you for this resource. lt got me pointed in the right direction for sure…something worth it’s weight in gold in PayPal’s world! :P

      • Brent says:

        Apparently you can only get profile details for users created by the Classic API

        Thanks for sharing. Sounds about right for PayPal. :(

        worth it to get rid of PayPal.

        Amen.

  14. Umar says:

    Excellent!!! Its very comprehensive and I could implement in my application in just 10 minutes

  15. Mahavir says:

    Hi
    I AM Getting Error :
    [L_ERRORCODE0] => 11552
    [L_SHORTMESSAGE0] => Invalid profile ID
    [L_LONGMESSAGE0] => The profile ID is invalid
    [L_SEVERITYCODE0] => Error

  16. Bill Kennedy says:

    I never created a PayPal account, nor used one, but periodically I get an email saying the account is suspended because someone tried to use the account. I cannot log on to unsubscribe because I do not because of course PayPal doesn’t recognize my name, etc. I’d like to wipe out this annoyance, but how? PayPal will not make this easy

    • Brent says:

      Hi Bill, I’d contact PayPal customer support to ask why you receive these emails, but they may just be spam/phishing emails not originating form PayPal.

  17. great job putting this together, problem with paypal is not lack of information its rather to much of information without any productive direction

  18. > Compounding the problem above, PayPal sends no IPN message when a profile is suspended.

    What about subscription cancellations? Is “No IPN” just for suspension, or for the whole shebang?

  19. Sooraj says:

    Hi I’m getting this error,
    [TIMESTAMP] => 2014-04-04T06:20:11Z
    [CORRELATIONID] => e2de55e795808
    [ACK] => Failure
    [VERSION] => 76.0
    [BUILD] => 10433064
    [L_ERRORCODE0] => 11552
    [L_SHORTMESSAGE0] => Invalid profile ID
    [L_LONGMESSAGE0] => The profile ID is invalid
    [L_SEVERITYCODE0] => Error

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s