Subscription Billing Integration Guide

Learn how to accept payments for subscription, including: creating subscription links; processing subscription-related IPN messages from OKPAY on your website/business logic; testing and troubleshooting your subscriptions code; and generally managing the list of your subscriptions or individual subscribers.

Table of contents:

§1. General Information

OKPAY provides great functionality for subscription billing – i.e. accepting pre-scheduled payments from your customers. Your clients will be able to pay for subscription not only by using the OKPAY account balance but also with any other supported payment method activated for your business.

Note: Subscription Billing is available to any verified OKPAY user who selected a Business account type. Read more in the General Reference Guide for Merchants. Some general principles described in the Payment Integration Guide are also suitable for subscriptions.

Here are some of the Subscription Billing features:

  • You can set up a price to charge, or make it free.
  • The subscription billing has 2 cycles. The trial cycle, which always has a limited number of renewals, or the regular cycle which can be limited as well as unlimited.
  • A cycle duration can be set in days, weeks, months and years;
  • A various price for each individual cycle can be set up in any currency supported by OKPAY.
  • A subscription can be used for selling digital content, service, physical goods (the client will be asked to provide a shipping address), and donations.
  • Temporary suspensions (scheduled payments will not be charged) and modifications can be used, with the consent of the client.
  • You can select the number of reattempts allowed to charge a subscription payment, from a subscription cancellation with the first failed payment, up to an unlimited number of reattempts.
  • You can ask a client to pay not only a current subscription payment but also to include all previously unpaid renewal payments up to the current bill, if you allow multiple reattempts.

A new subscription is created by placing a special link on the merchant's website. This link is similar to a payment link, but has a slightly different set of variables.

By clicking on the created link, customers are forwarded from the merchant's website to a subscription payment page, where it can be paid for using one of the payment methods. Subsequent subscription payments are charged directly from the client's e-wallet according to the schedule.

The merchant is notified by e-mail of any new subscriptions and all events related to future changes. Additionally, OKPAY sends IPN messages with all the necessary technical details to your website or back office application.

To manage subscribers, go to your Profile » Subscriptions Billing where you can easily find a list of all subscriptions and apply the required actions to a group of subscribers (or an individual subscriber) in a few mouse clicks. You can not only cancel or suspend a subscription but also modify its terms. This requires the client's consent.

Figure 1. Subscriptions billing dashboard.

Merchant's Dasboard

Almost any task that is done using the web interface can be also performed via OKPAY API.

We will explain these steps and features in more detail.

§2. Creating a New Subscription

To ensure your customers are able to subscribe and receive your goods and services on a schedule you have to create a special URL-link which then should be placed on your website. You can use the Button Generator or manually create a URL containing variables described in the Payment Links Variable Reference article of the Comprehensive Manual.

Note: Please learn more about general payment links and buttons usage in Comprehensive Manual, section Payment Links.

Example 1. The simplest subscription link contains only a few parameters:

https://checkout.okpay.eu?ok_receiver=OK702746927&ok_kind=subscription&ok_currency=EUR&ok_s_title=My+Very+Simple+Subscription&ok_s_regular_price=7&ok_s_regular_cycle=1+W

We have a subscription link (ok_kind=subscription) named "My Very Simple Subscription". The subscription payment is 7.00 EUR per week and the receiver wallet is OK702746927.

Example 2. Let's have a look at a more complicated example. We created a subscription with a setup price of $55.00. It has a 2 week of a free trial period, with a subscription payment of $99.00 charged every 2 weeks thereafter. After 11 regular cycles the subscription will be terminated. If all of the announced subscription cycles are paid by the client a merchant should receive $1,144.00 USD by the end of 24 weeks.

https://checkout.okpay.eu?ok_receiver=OK702746927&ok_kind=subscription&ok_currency=USD&ok_s_title=My+Second+Subscription&ok_s_setup_price=55&ok_s_regular_price=99&ok_s_regular_cycle=2+W&ok_s_regular_count=11&ok_s_trial_count=1&ok_s_trial_cycle=2+W&ok_s_trial_price=0

Figure 2. Subscription button graphics.

Subscription Buttons

Figure 3. Checkout page interface for subscription payment.

Subscription Checkout

In the examples mentioned above, if a customer encounters a payment issue and misses any of the cycles, only the cost of the next cycle needs to be paid in order to resume subscription. If you want the client to pay all the skipped payments before the subscription is renewed then add the ok_s_reattempt_accum=1 variable to the subscription link.

By default, subscriptions are created in such a way that when there is a payment issue, the system will attempt to charge the required amount every day an infinite number of times. If you want to set the number of retries manually - use the variable ok_s_reattempt_days. A value of 0 means that in the event of a payment issue the subscription will be terminated immediately. Any positive value will set the exact number of attempts. Payment attempts are done automatically once a day.

§3. Handling IPN Messages

Subscription event handling in IPN messages allows you to fully automate your work with subscribers. Get an instant IPN notification if you have a new subscriber, if there is a problem with payment, if payment is successfully charged, if a subscription is canceled, and so on. For suspended or canceled subscriptions OKPAY also sends a special IPN variable, ok_s_active, which indicates the real subscription term is still active. You receive an additional IPN message when the term of subscription for a particular client is completed: ok_s_event=ended.

Hereafter we will describe the specific features of IPN with regard to the receiving of payments. If you are looking for more information on IPN, such as examples of basic-code writing rules, testing, or IPN messages history, please refer to the IPN section of the Comprehensive Manual.

§3.1. IPN Handler Setup

In order to use the IPN, first of all, you need to inform OKPAY where the IPN handler script is located on your server:

  • In wallet settings (Profile » Wallets and Currencies » select wallet and click on Edit » Integration tab. Under "Payment Notification (IPN)" enter URL address of your IPN handler;
  • Dynamically in the payment link by specifying the IPN handler address in ok_ipn.

Read more on how to setup IPN handler, subsection of the Comprehensive Manual.

§ 3.2. Writing Handler Code

General code-writing rules for IPN handler can be found in the Implementing a Generic IPN Listener, subsection of the Comprehensive Manual.

To identify an incoming IPN as a subscription payment message you need to look up the ok_txn_kind variable which should be equal to ok_txn_kind=subscription. Then you need to examine the ok_s_event variable in order to determine an event type. They are as follows:

  • Started - a new subscriber is added. This event is always followed by a "payment", as the client should make a payment upon subscribing.
  • Payment - the subscription payment is received. In the case of the first payment for a new subscription it is possible to receive a zero payment. For example, if the subscription's setup price and the trial period are zero.
  • Failed - the client does not have enough funds on the wallet balance to pay for the subscription. Depending upon the value of ok_s_reattempt_days attempts may continue or the subscription will be terminated (the status changed to canceled).
  • Canceled - subscription is canceled for one of the following reasons: there is a problem with payment (all the reattempts has been used); the client decided to terminate subscription; or the merchant modified the subscription terms without them being approved by the client within 30 days.
  • Suspended - the subscription is temporarily suspended by either the client or the merchant. Note that only the party who has suspended the subscription has the ability to resume it.
  • Modified - the subscription terms are modified by the merchant and require the client's approval.
  • Expired - the last subscription cycle is completed (the subscription had a finite number of regular cycles).
  • Ended - the subscription came to an end (expired) or is no longer active (a suspension, cancellation, modification, or payment problem) and the actual expiry date is over.

Figure 4. A sequence of status transitions (ok_s_status).

Status Transitions

Figure 5. A sequence of events transitions (ok_s_event).

Event Transitions

When processing an IPN subscription, ok_s_active is an important variable to remember. This indicates if a subscription is active. For example, if you cancel a subscription, the status is changed to canceled, but if the subscription was active and paid for a week ahead, the parameter ok_s_active=1, along with the status "canceled", is sent. This means that the subscription is still active and will end when the paid period is over. This is indicated in the variable ok_s_validuntil.

In Code Samples in Various Programming Languages you can find a detailed example of what an IPN handler for subscription payments should look like. You can use this example to create your own handler.

§ 3.3. Tips for Safe Handling of Subscription Messages

Firstly, to ensure the safe and proper operation of your subscription payments, verify that the received IPN is genuine, that it is sent by the OKPAY server and that it has not been handled before (check ok_ipn_id). Details of this can be found on the Implementing a Generic IPN Listener, subsection of the Comprehensive Manual.

Close attention should be paid to the event processing variable ok_s_event=payment. You should also check for the following:

  1. That the operation status is ok_txn_status=completed.
  2. That this operation has not been carried out earlier. You can do this by checking the variable ok_txn_id.
  3. That the recipient's wallet number ok_receiver_wallet or Account ID ok_receiver_id belongs to you.
  4. That the incoming amount is correct (ok_txn_net for the amount minus the OKPAY fee or ok_txn_gross for the total amount charged).
  5. That the payment currency, ok_txn_currency, is correct. Also make sure that the value of the transferred funds, ok_txn_fee, meets with the one you used in reference to a payment.

We recommend that you start writing code for your handler based on the sample in Code Samples in Various Programming Languages.

§ 3.4. Testing Subscription IPN

OKPAY contains an IPN simulator to test the message handling of your scripts. This functionality is described on the IPN Testing, subsection of Comprehensive Manual.

The IPN payments simulator can be used for the initial testing of your source code. For a more thorough and practical testing of your business logic please refer to the Final Testing of Subscription Payments.

§4. Managing Subscriptions via API

OKPAY system allows for a full automated process of subscription payments for your business. API functions and IPN are parts of a single integration system.

You can manage your subscribers not only via the OKPAY web interface (see Managing Subscriptions via Web Interface) but also inside the information system of your organisation. You can write a code that uses the appropriate API functions.

The subscription payment's API contains the following functions:

For additional information, click on the function name.

Quite like using the OKPAY web interface, API functions allow you to have complete control over absolutely all the subscription properties.

A detailed example of API function calls in response to respective IPN messages can be found in Code Samples in Various Programming Languages.

§5. Managing Subscriptions via Web Interface

The OKPAY Subscriptions Billing Dashboard (this can be accessed from your Profile » Subscriptions Billing) allows you to manage subscribers and their subscriptions via the web interface.

You can easily view a list of all subscriptions and take the following actions with your subscribers:

  • Suspend subscription. Regular scheduled payments will not be charged while it is suspended. Subscription can be suspended either by the merchant or by the client. Note that only the party who suspends the subscription has the ability to resume it.
  • Change your subscription terms. Modify the subscription after it has been created. With the consent of the client (using the OKPAY website interface) new subscription terms will take effect starting from the next scheduled renewal under the old conditions.
  • Completely cancel subscription. Further renewal of this subscription is not possible; from this moment no subscription payments will be charged. To resume the subscription the client must subscribe at the merchant's website again.
  • (Only for individual subscription) In case of failed subscription payments the merchant may also try to reinitiate subscription payment from the customer's wallet. The system automatically attempts to charge a payment once a day.

§5.1. Statistics, Search and Filtering Options

At the top of the page you can find useful statistical information about your subscribers, specifically, the number of active, unpaid, suspended, modified, ended and canceled subscriptions.

Figure 6. Subscription search.

Search

The search and filtering options are located beneath the statistics. These include:

  • The subscription status (active, suspended, unpaid, modified, canceled, suspended).
  • The ID of a specific subscription. Each OKPAY subscription has a unique identifier.
  • The subscription title (there are several different subscription types, and each subscription has a unique name).
  • The period of subscription creation. You can select any date range.
  • The e-mail address, phone number or wallet ID of subscribed client.

§5.2. Managing Individual Subscription

Use the search option to find a subscription and click on the subscription ID.

Figure 7. Link to the details of subscription is highlighted red (column ID).

Subscription Details Link

After clicking on subscription ID you will be forwarded to the subscription details page. This contains information on the client, the parameters of the subscription, the date and amount of upcoming renewal payments, the payments received from the client, etc.

Figure 8. Subscription details page with merchant's options.

Subscription Details Page

For the selected subscription you can perform following actions (see figure above):

  • Suspend the subscription;
  • Cancel the subscription;
  • Modify the subscription;
  • (For subscriptions with a payment issue) Manually reinitialise payment attempt.

§5.2.1. Suspending a Subscription

By clicking on the appropriate button (and confirming in the pop-up dialogue box) you can temporarily suspend the subscription renewal. This means that upcoming renewal payments will not be charged.

This functionality can be useful if you are providing customers with the access to regularly updated content available via subscriptions, and for some reason the content will not be updated for a while (for example, if you go on holiday).

Attention: If you specify a variable ok_s_reattempt_accum=1 then the suspension will not work, since the skipped payments will still be charged in the future anyway.

You can resume the suspended subscription at any moment by clicking on the Resume subscription button (this appears in the same place as the Suspend subscription button). Note that the client may also suspend the subscription, which only the client can then resume.

§5.2.2. Cancelling a Subscription

Click on the Cancel subscription button and confirm that you would like to completely stop the subscription. Canceled subscriptions can no longer be reactivated. Clients would have to subscribe to the merchant again in order to restore the subscription membership.

§5.2.3. Modifying a Subscription

The merchant can at any time modify the terms of subscription for a customer. For example, you may want to increase the cost of your subscription, or change the subscription cycle duration.

The following parameters can be changed:

  • Title
  • The number of reattempts to charge the subscription payment
  • Whether to add the skipped payments to the next subscription payment
  • All the values ​​of trial and regular cycles: the cycle duration, the number of cycles and the amount

Hint: The merchant is able to send a message to a client with a comment on the subscription modification. The comment is sent to a client directly by e-mail address as well as being displayed in OKPAY account on the new terms approval page (see Figure 11 - "Comment from Merchant").

Figure 9. Subscription modification window.

Subscription Modification Window

The subscription status is changed to Modified when the settings are changed. Further subscription payments are not charged (this is the same as for the Suspended status).

The client receives an e-mail and a notification message which is displayed after login (see Figure 10), indicating that the merchant wishes to change the subscription settings. The client can either accept the new terms or reject them (see Figure 11).

Important: The client has 30 days to accept or to reject the new terms offered by the merchant. If the client will not take proper action within this period then subscription will be automatically canceled.

Figure 10. Subscription terms update message in client's account.

Subscription Modification Message

By clicking on the confirmation link the client is forwarded to a subscription details page where the updated terms can be found. The table contains the current (old) and the updated (new) parameters. See figure below.

Figure 11. Revised subscription terms dialogue window.

Subscription Modification Confirmation

If a customer clicks on the "Reject" button, the subscription status will be changed to Canceled. If the client accepts the new terms (by clicking the "Accept" button) the new subscription renewal payments will be applied ​​only after the current cycle has been paid by the client (on the previous terms). The next cycle will feature updated subscription terms.

§5.2.4. Manual Payment Attempt Initialization

In some cases, the merchant can click on the "Collect Balance" button for the unpaid subscription to charge for a subscription payment using the client's wallet. The system automatically attempts to charge a payment once a day.

Figure 12. Charging of the skipped subscription payments.

Collect Balance

If a client has enough funds in that particular wallet, the renewal payment will be processed.

§5.3. Mass Subscriptions Management

On the Subscriptions Billing page you can select your subscriptions and perform the necessary actions by using the options available.

Keep in mind that for most activities, barring some obvious exceptions, selected subscriptions must share the same status.

Figure 13. Group managing of subscriptions.

Group Managing

Generally, group managing is similar to individual changing. The cancel, suspend and reactivate options work just the same way.

If you select several subscriptions and click on "Modify", the system will display a standard dialogue, "Modify Subscription", but only the fields with identical information for every subscription will be filled. The varying fields will be blank. You have to set your desired values ​​for the selected subscriptions and click on the Send for approval button.

§6. Final Testing of Subscription Payments

As explained in Testing Subscription IPN, you can check the basic operation of your handler by using the IPN simulation service.

In addition, for a final and more complete testing of your code, as well as your business-logic operation, you may need an additional real-world testing with real clients.

We recommend that you create a test account, which can be used to perform subscription payments.

If you have any questions or problems feel free to contact OKPAY Support Service. Our technicians will be happy to help.

§7. Code Samples in Various Programming Languages

There are, for your reference, several examples of subscription payments with OKPAY provided in popular programming languages. These include:

  1. An IPN message handler example.
  2. An API class example for subscription payments.
Sample in PHP
 <?php
 /*
     Subscriptions OKPAY API class
 */
 class OKPAY_Subscriptions {
     private $WalletID       = 'OK123456789';           // Wallet ID of service owner
     private $APIPassword    = 'Your API Password';     // API password
     private $SoapClient;                               // SOAP client for verification API
     private $Key;                                      // Security key
     public  $LogFilename    = 'SubscriptionTest.txt';  // Log file name
     public  $Debug          = false;                   // Is debug mode on?
 
 
     // Pre-defined variables
     public function __construct() {
         $this->Key = strtoupper(hash("sha256", $this->APIPassword.":".gmdate("Ymd:H")));
         $this->SoapClient = new SoapClient("https://api.okpay.com/OkPayAPI?wsdl");
     }
 
     // IPN verification function
     public function VerifyIPN() {
         $Request = 'ok_verify=true';
         foreach ($_POST as $Key => $Value) {
             $value = urlencode(stripslashes($Value));
             $Request .= "&$Key=$Value";
         }
 
         $header  = "POST /ipn-verify HTTP/1.0\r\n";
         $header .= "Host: checkout.okpay.eu\r\n";
         $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
         $header .= "Content-Length: " . strlen($Request) . "\r\n\r\n";
         $fp = fsockopen ('checkout.okpay.eu', 80, $errno, $errstr, 30);
 
         if (!$fp) {
             $this->log('Cannot connect to OKPAY');
             return false;
         } else {
             fputs ($fp, $header . $Request);
             while (!feof($fp)) {
                 $res = fgets ($fp, 1024);
                 if (strcmp ($res, "VERIFIED") == 0 && $_POST["ok_txn_kind"] == "subscription") {
                     // IPN verified
                     $this->log('IPN verified');
                     $this->log('Request - '.$Request);
                     return true;
                 } elseif (strcmp ($res, "INVALID") == 0) {
                     // IPN invalid
                     $this->log('IPN INVALID');
                     $this->log('Request - '.$Request);
                     return false;
                 } elseif (strcmp ($res, "TEST")== 0 && $_POST["ok_txn_kind"] == "subscription") {
                     // Test IPN
                     $this->log('IPN TEST');
                     $this->log('Request - '.$Request);
                     return true;
                 }
             }
             fclose ($fp);
         }
     }
 
     // Get subscription information
     public function SubscriptionGet($SubscriptionID = '') {
         try {
             $obj = new stdClass();
             $obj->WalletID = $this->WalletID;
             $obj->SecurityToken = $this->Key;
             $obj->SubscriptionID = $SubscriptionID;
             $webService = $this->SoapClient->Subscription_Get($obj);
             return $webService->Subscription_GetResult;
         } catch (Exception $e) {
             $this->log(__LINE__.": Caught exception: ".  $e->getMessage());
             return false;
         }
     }
 
     // Get subscriptions list
     public function SubscriptionsFilter($Title = '', $From = '', $Till = '', $Statuses = array()) {
         try {
             $obj = new stdClass();
             $obj->WalletID = $this->WalletID;
             $obj->SecurityToken = $this->Key;
             $obj->Title = $Title;
             $obj->From = $From;
             $obj->Till = $Till;
             $obj->Statuses = $Statuses;
             $webService = $this->SoapClient->Subscriptions_Filter($obj);
             return $webService->Subscriptions_FilterResult;
         } catch (Exception $e) {
             $this->log(__LINE__.": Caught exception: ".  $e->getMessage());
             return false;
         }
     }
 
     // Update subscription parameters
     public function SubscriptionUpdate($Subscription, $Comment = '') {
         try {
             $obj = new stdClass();
             $obj->WalletID = $this->WalletID;
             $obj->SecurityToken = $this->Key;
             $obj->Sub = $Subscription;
             $obj->Comment = $Comment;
             $webService = $this->SoapClient->Subscription_Update($obj);
             return $webService->Subscription_UpdateResult;
         } catch (Exception $e) {
             $this->log(__LINE__.": Caught exception: ".  $e->getMessage());
             return false;
         }
     }
 
     // Get subscription operations
     public function SubscriptionGetOperations($SubscriptionID = '') {
         try {
             $obj = new stdClass();
             $obj->WalletID = $this->WalletID;
             $obj->SecurityToken = $this->Key;
             $obj->SubscriptionID = $SubscriptionID;
             $webService = $this->SoapClient->Subscription_Get_Operations($obj);
             return $webService->Subscription_Get_OperationsResult;
         } catch (Exception $e) {
             $this->log(__LINE__.": Caught exception: ".  $e->getMessage());
             return false;
         }
     }
 
     private function log($message) {
         if($this->Debug == true) {
             file_put_contents($this->LogFilename, date("d.m.Y H:i:s").': '.$message."\r\n", FILE_APPEND);
         }
     }
 }
 ?>
 
 <pre>
 <?php
 /*
 * Dummy function, you need to use your own
 */
 function GetOrder($SomeID){
     // Simulates getting order data
     $Order = new stdClass();
     $Order->ID = 4567;
     $Order->Currency = 'USD';
     $Order->IsCmpleted = false;
     $Order->Fee = '1';
     $Order->Amount = 123.45;
     return $Order;
 }
 
 /*
 * Sample for API functions
 */
 $API = new OKPAY_Subscriptions();                 // Initialize
 $API->Debug = true;                              // Enable debug mode
 $API->LogFilename = './subscription_debug.txt'; // Change debug filename
 
 // Some subscription ID
 $SubscriptionID = 12345;
 
 // Get subscription info
 $Subscription = $API->SubscriptionGet($SubscriptionID);
 #print_r($Subscription);
 
 // Change subscription regular price
 $Subscription->RegularPrice += 0.01;
 
 // Change the number of regular cycles of the subscription
 $Subscription->RegularCount = 50;
 
 // Change subscription status from "Active" to "Modified"
 // IMPORTANT! You cannot modify inactive subscription!
 $Subscription->Status = "Modified";
 
 // Save subscription changes with comments
 $Subscription =  $API->SubscriptionUpdate($Subscription, 'Subscription update');
 print_r($Subscription);
 
 // Get subscription operations
 $Operations = $API->SubscriptionGetOperations($SubscriptionID);
 print_r($Operations);
 
 // Get subscriptions list                 title part       created from           created till        statuses (`ok_s_status`)
 $Subscriptions = $API->SubscriptionsFilter('My test subscription', '2013-01-01 00:00:00', '2013-12-31 23:59:59', array());
 print_r($Subscriptions);
 
 /*
 * Sample IPN handler
 */
 if($_SERVER['REQUEST_METHOD'] === 'POST') {           // POST method - possibly IPN request
     if($API->VerifyIPN()) {                           // If IPN message is valid
         if($_POST['ok_txn_kind'] == 'subscription') { // This is subscription IPN request
             $ok_s_id = (integer)$_POST['ok_s_id'];    // Get subscription ID
             switch($_POST['ok_s_event']) {
                 case 'started':
                     /*
                         New subscription registered
                     */
 
                     if($Subscription = $API->SubscriptionGet($ok_s_id)) {
                         /*
                             Store information about subscription in DB
                         */
 
                     }
 
                     break;
                 case 'payment':
                     /*
                         Successfull subscription payment received
                     */
                     $Order = GetOrder($_POST['ok_invoice']); // Get order details (dummy function)
                     if(
                         $_POST['ok_txn_status']      == 'completed' &&      // Ensure that status of payment is `completed`
                         $_POST['ok_receiver_wallet'] == 'OK123456789' &&    // Check that wallet of receiver is yours
                         $_POST['ok_txn_net']         == $Order->Amount &&   // Check payment amount
                         $_POST['ok_txn_currency']    == $Order->Currency && // Check payment currency
                         $_POST['ok_txn_fee']         == $Order->Fees &&     // Check fees configuration
                         $Order->IsCmpleted           == false               // Check that order is not processed yet
                     ){
                         /*
                             Do something...
                         */
 
                     }
 
                     break;
                 case 'failed':      // Customer does not have enough funds for the subscription payment
                 case 'canceled':   // Subscription cancelled (payment failed / merchant or customer manually cancelled the subscription)
                 case 'suspended': // Subscription suspended by client or merchant
                 case 'modified': // Subscription terms change requested and pending client approval
                 case 'expired': // Last regular cycle is finished
                 case 'ended':  // The term of the subscription is actually finished
                     /*
                         In all of these cases you should stop client subscription with ID = $_POST['ok_s_id']
                     */
                     if($Subscription = $API->SubscriptionGet($ok_s_id)) {
                         /*
                             You can use $Subscription->Client->Email or $Subscription->Client->WalletID to identify your client
                         */
 
                     }
                     break;
 
                 default:
                     break;
             }
         }
     }
 }
 
 ?>
Sample in C#
 using System;using Web.Common.Translation;using Web.Common;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
 namespace Developers.OKPAY.COM
 {
 	///Add Service Reference to your C# project (https://api.okpay.com/OkPayAPI)
 
     /// <summary>
     /// Subscription OKPAY API class
     /// </summary>
     public class OKPAY_Subscription
     {
         /// <summary>
         ///  Wallet ID of service owner
         /// </summary>
         private string WalletID = "OK123456789";
 
         /// <summary>
         /// API password
         /// </summary>
         private string APIAccessPassword = "Your API Password";
 
 
 
         /// <summary>
         ///  Get subscription information
         /// </summary>
         public OKPAY_API.Subscription SubscriptionGet(long subscriptionID)
         {
             string securityToken = this.SecurityToken;
             using (var proxy = new OKPAY_API.I_OkPayAPIClient())
             {
                 try
                 {
                     var subscription = proxy.Subscription_Get(WalletID, securityToken, subscriptionID);
                     return subscription;
                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.ToString());
                     throw ex;
                 }
             }
         }
 
 
         /// <summary>
         ///  Get subscriptions list
         /// </summary>
         public IEnumerable<OKPAY_API.Subscription> SubscriptionsFilter(string title, string from, string till, OKPAY_API.SubscriptionStatuses[] statuses)
         {
             string securityToken = this.SecurityToken;
             using (var proxy = new OKPAY_API.I_OkPayAPIClient())
             {
                 try
                 {
                     var subscriptions = proxy.Subscriptions_Filter(WalletID, securityToken, title, from, till, statuses);
                     return subscriptions;
                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.ToString());
                     throw ex;
                 }
             }
         }
 
 
         /// <summary>
         /// Update subscription parameters
         /// </summary>
         public OKPAY_API.Subscription SubscriptionUpdate(OKPAY_API.Subscription sub, string comment)
         {
             string securityToken = this.SecurityToken;
             using (var proxy = new OKPAY_API.I_OkPayAPIClient())
             {
                 try
                 {
                     var subscription = proxy.Subscription_Update(WalletID, securityToken, sub, comment);
                     return subscription;
                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.ToString());
                     throw ex;
                 }
             }
         }
 
 
         /// <summary>
         /// Get subscription operations
         /// </summary>
         public IEnumerable<OKPAY_API.TransactionInfo> SubscriptionGetOperations(long subscriptionID)
         {
             string securityToken = this.SecurityToken;
             using (var proxy = new OKPAY_API.I_OkPayAPIClient())
             {
                 try
                 {
                     var transactions = proxy.Subscription_Get_Operations(WalletID, securityToken, subscriptionID);
                     return transactions;
                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.ToString());
                     throw ex;
                 }
             }
         }
 
 
 
 
 
 
         public bool VerifyIPN(byte[] parameters)
         {
             var req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("https://checkout.okpay.eu/ipn-verify");
 
             //Set values for the request back
             req.Method = "POST";
             req.ContentType = "application/x-www-form-urlencoded";
             string strRequest = Encoding.UTF8.GetString(parameters);
             strRequest += "&ok_verify=true";
             req.ContentLength = strRequest.Length;
 
             //Send the request to OKPAY and get the response
             using (var streamOut = new System.IO.StreamWriter(req.GetRequestStream()))
             {
                 streamOut.Write(strRequest);
                 streamOut.Close();
                 using (var streamIn = new System.IO.StreamReader(req.GetResponse().GetResponseStream()))
                 {
                     string strResponse = streamIn.ReadToEnd();
                     streamIn.Close();
 
                     if (strResponse == "VERIFIED")
                     {
                         return true;
                     }
                     else if (strResponse == "INVALID")
                     {
                         return false;
                     }
                     else if (strResponse == "TEST")
                     {
                         return true;
                     }
                     else
                     {
                         return false;
                     }
                 }
             }
 
         }
 
 
         private string SecurityToken
         {
             get
             {
                 return HashStringSHA256(this.APIAccessPassword + DateTime.UtcNow.ToString(":yyyyMMdd:HH"));
             }
         }
 
         private string HashStringSHA256(string Value)
         {
             using (System.Security.Cryptography.SHA256CryptoServiceProvider x = new System.Security.Cryptography.SHA256CryptoServiceProvider())
             {
                 byte[] data = System.Text.Encoding.ASCII.GetBytes(Value);
                 data = x.ComputeHash(data);
                 return BitConverter.ToString(data).Replace("-", "");
             }
         }
     }
 
     public class DummyOrder
     {
         public long ID { get; set; }
         public decimal Amount { get; set; }
         public string  Currency { get; set; }
         public decimal  Fees { get; set; }
         public bool IsCompleted { get; set; }
     }
 
     public class DummyClass
     {
         public static DummyOrder GetDummyOrder(long orderID)
         {
             DummyOrder order = new DummyOrder();
             order.ID = 4567;
             order.IsCompleted = false;
             order.Currency = "USD";
             order.Fees = 1;
             order.Amount = 123.45M;
             return order;
         }
     }
 
     /// <summary>
     /// Sample IPN handler
     /// </summary>
     public class IPN_Handler
     {
         OKPAY_Subscription api_subscription = new OKPAY_Subscription();
 
         public void ProcessRequest(System.Web.HttpContext context)
         {
             Dictionary<string, string> parameters = new Dictionary<string, string>();
             foreach (var item in context.Request.Form)
             {
                 parameters[item.ToString()] = System.Web.HttpUtility.HtmlDecode(context.Request.Form[item.ToString()]);
             }
 
             byte[] param = context.Request.BinaryRead(context.Request.ContentLength);
             var ipnTestResult = api_subscription.VerifyIPN(param);
             if (ipnTestResult)                                    // If IPN message is valid
             {
                 if (parameters["ok_txn_kind"] == "subscription")  //This is subscription IPN request
                 {
                     OKPAY_API.Subscription subscription;
 
                     long subscriptionID = long.Parse(parameters["ok_s_id"]);  // Get subscription ID
                     switch (parameters["ok_s_event"])
                     {
                         case "started":
                             /*
                                 New subscription registered
                             */
                             subscription = api_subscription.SubscriptionGet(subscriptionID);
 
                             /*
                                  Store information in DB
                             */
                             break;
 
                         case "payment":
                            /*
                                 Successfull subscription payment received
                            */
                             var orderID = long.Parse(parameters["ok_invoice"]);
                             var order = DummyClass.GetDummyOrder(orderID); // Get order details (dummy function)
                             if(
                                 parameters["ok_txn_status"] == "completed" && // Ensure that status of payment is `completed`
                                 parameters["ok_receiver_wallet"] == "OK123456789" && // Check that wallet of receiver is yours
                                 decimal.Parse(parameters["ok_txn_net"])==order.Amount &&  // Check payment amount
                                 decimal.Parse(parameters["ok_txn_fee"])==order.Fees && // Check fees configuration
                                 parameters["ok_txn_currency"] == order.Currency && // Check payment currency
                                 order.IsCompleted == false // Check that order is not processed yet
                              )
                                 {
                                     /*
                                         Do something...
                                     */
                                 }
                             break;
 
                         case "failed":      // Customer does not have enough funds for the subscription payment
                         case "canceled":   // Subscription cancelled (payment failed / merchant or customer manually cancelled the subscription)
                         case "suspended": // Subscription suspended by client or merchant
                         case "modified": // Subscription terms change requested and pending client approval
                         case "expired": // Last regular cycle is finished
                         case "ended":  // The term of the subscription is actually finished
                         /*
                             In all of these cases you should stop client subscription with ID = parameters["ok_s_id"]
                         */
                          subscription = api_subscription.SubscriptionGet(subscriptionID);
                          /*
                             You can use subscription.Client.Email or subscription.Client.WalletID to identify your client
                         */
 
                          break;
                         default:
                             break;
                     }
                 }
 
             }
         }
 
 
 
     }
 
 
 
 }
OKPAY RSS
OKPAY Forum
OKPAY on Facebook
OKPAY on Livejournal
OKPAY on Twitter
OKPAY on VK
OKPAY on Linkedin
OKPAY on Youtube