Recurring Payments and IsGenuineEx()

Hi,

We are in process of integrating Java app, LimeLM and PayPal to sell desktop app on a subscription based model. The customer selects Monthly, Quarterly or Yearly subscription plan and after payment gets license key with custom "expires" field. This field we check in the app to see if user is allowed to use it.

Can you please provide source code example (or pseudo code) how to call IsGenuineEx() 1 day after PayPal tries to collect next payment? PayPal does not operate on daily basis (as IsGenuineEx do) and adjusts monthly billing cycle depending whether it is 29th, 30th or 31th, and etc.

https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/subscribe_buttons/#id08ADF600ZPM

Thanks.

Well, the best way to do this is have IsGenuineEx() called every 30 days (every month but February has 30 or more days). Also, when the "expires" fields has expired then recheck by calling IsGenuine. Something like this (in pseudo code):




if (TurboActivate.IsGenuineEx(30, 14) == Genuine || GenuineFeaturesChanged || InternetError)){    // User is activated but the "expires" feature may or may not be activated    string expires = TurboActivate.GetFeatureValue("expires");


    if (TurboActivate.IsDateValid(expires))    {        // the user hasn't expired. continue on with your program as usual.    }    else    {        //TODO: show a dialog box that says the trial has expired. Have a button that they can click        //            something like "Click after purchasing a renewal". That button will call IsGenuine()        //            which will in turn download the latest features (license fields) if they've changed.    }}




Those are the basics. Does that make sense?

😲

1) Did you read how PayPal billing cycle works?2) February has not 30, but 28 or more days. Other months have 30 or 31 days. Calling IsGenuineEx() each 30 days will shift the check time from month to month, so it will not be 24 hours after PayPal billed the customer.3) You saying to check each 30 days but wrote IsGenuineEx(90, 14).

Seems like support is not very careful.

February has not 30, but 28 or more days. Other months have 30 or 31 days.

Yes, that's exactly what this means:

every month but February has 30 or more days

"but" means "except" (they're synonyms in this context). We said the exact same thing. I'm not sure why you're upset about this.

Calling IsGenuineEx() each 30 days will shift the check time from month to month, so it will not be 24 hours after PayPal billed the customer.

Right, but you can use our functions to fill in the gaps. That's the point of the pseudo code. If the "expires" has expired, then call IsGenuine() to recheck immediately (not IsGenuinEx). If that IsGenuine call doesn't return "features changed" then that means the user is still "expired" and thus you should show them a dialog box that says "buy a renewal here" or something like that.

Our systems doesn't work on a monthly basis, and PayPal presumably doesn't work on a daily basis like our system.

That's why we have lots of different functions so you can use TurboActivate for whatever your needs are.

3) You saying to check each 30 days but wrote IsGenuineEx(90, 14).

Sorry, that was a typo. I've fixed it.

"For monthly billing cycles, recurring payments are collected on the same day of the month. If the initial recurring payment falls on the 31st, PayPal eventually adjusts the billing cycle to the 1st of the month."

This means that monthly billing cycle could be something from 29 to 31 day. If we will call IsGenuineEx(31,...) to be safe for the case when customer is successfully billed each month we will eventually push our checking date further from actual date when PayPal bills the customer.

So after maybe a year we can have something about a week time gap when PayPal tries to bill the customer and when we call IsGenuineEx. If we multiply this week by number of customers we get a lot of money lost here.

We don't want to call IsGenuineEx every and even prefer to make offline activations and IsGenuine check because customer operates with lot of money via the app. So we need to minimize app connections to 3rd party servers like yours.

Thanks.

And we don't want to prompt user say on October 4th, when his next billing is on October 5th.

OK, then the only option for you is to use plain old IsGenuine() and you have to keep track of when to call it. Because even PayPal's method of billing isn't "truly" monthly. It's a special case monthly (namely, the 31st day is a special case).

Short answer: you keep track of when to call IsGenuine().

OK, Thanks.

Sam, can you please describe in pseudo code or flow chart the algorithm of IsGenuineEx() function. Does it calls IsActivated() and contacts LimeLM servers each time? From which date IsGenuineEx(30,) will count these 30 days? Will call to CheckAndSavePKey() or Activate() affect this date(s)?

If you're calling IsGenuine() yourself without using our tracking then it's entirely up to you how you do it.

if (IsMonthlyRenewalDate){    // call IsGenuine()}else{    // call IsActivated()}

You can see when to call IsGenuine based on the "expires" value.

No, we decided to use FastSpring (it has 30 days billing cycle) and so we can call IsGenuineEx(30,...). But I want to know how it works.

The TurboActivate.h file contains in-depth descriptions of all the functions. Here's IsGenuineEx():

Checks whether the computer is genuinely activated by verifying with the LimeLM serversafter a certain number of days you specify.


This is meant as a replacement of both IsActivated() and IsGenuine(). Call this at thetop of your program and let IsGenuineEx() handle all the details.


This differs with IsGenuine() in 3 major ways:


     1. You can specify how often to verify with the LimeLM servers and it handles        all the date tracking behind the scenes.




     2. IsGenuineEx() prevents your app from hammering the end-user's network after        and TA_E_INET error return code by not checking with the LimeLM servers until        at least 5 hours has passed. If you call IsGenuineEx() after a TA_E_INET return        and before 5 hours has elapsed then this function will return TA_E_INET_DELAYED.


        (If you give the user the option to recheck with LimeLM, e.g. via a button        like "Retry now" then call IsGenuine() to immediately retry without waiting 5 hours).




     3. If a TA_E_INET error is being returned, and the grace period has expired,        then IsGenuineEx() will return TA_FAIL. IsGenuineEx() will continue to try        contacting the LimeLM servers on subsequent calls (5 hours apart), but you        should treat the TA_FAIL as a hard failure.




Returns: TA_OK or TA_E_FEATURES_CHANGED on success. Handle TA_E_INET and TA_E_INET_DELAYED as warnings that         you should let the end user know about.


         Handle all other return codes as failures.


Possible return codes: TA_OK, TA_FAIL, TA_E_ACTIVATE, TA_E_INET, TA_E_GUID                       TA_E_PDETS, TA_E_COM, TA_E_EXPIRED, TA_E_REVOKED,                       TA_E_INVALID_ARGS, TA_E_INVALID_FLAGS, TA_E_IN_VM,                       TA_E_INET_DELAYED, TA_E_FEATURES_CHANGED,                       TA_E_ANDROID_NOT_INIT

Thanks,

We now trying to "Automate license generation with FastSpring". I used your FastSpring-PHP-Code.txt script but there is one problem. We have to set the expiration date of the key to 30 days after purchase date.

I'm not PHP developer and don't whether there are timezone differences between FastSpring and LimeLM servers. Can you please provide such PHP script?

//$feature_names = array('expires');//$feature_values = array('current_date + 30d');

Another question is how to update the expiration date of the same key on recurring payments?

P.S. Also we have 14 days trial set on LimeLM, so we do not use trial on FastSpring and send license key after user has subscribed.

I'm not PHP developer and don't whether there are timezone differences between FastSpring and LimeLM servers. Can you please provide such PHP script?
$feature_names = array('expires');$feature_values = array(date('Y-m-d', strtotime('+30 days')));

That's what you want. The timezones are whatever you want them to be. Read up on the date and strtotime() php functions.

Another question is how to update the expiration date of the same key on recurring payments?

All of the web API functions are here. What you'll want to use is the limelm.pkey.setDetails function.

OK, but I don't know how to use setDetails function in PHP. Am I asking so rare question that there is no source code example? Either everybodu knows how to implement recurring payment via FastSpring or nobody uses it 🙂

Am I asking so rare question that there is no source code example?

We haven't written the example code. Mostly because no one has asked, but also because the code would be nearly identical to the FastSpring example we already have. The only things you'd change are this:

  1. Retrieve the product key id of the user renewing their subscription.
  2. Set the new "expires date" using the limelm.pkey.setDetails function (instead of limelm.pkey.generate as seen in the existing example script).

See? Not a whole lot to see. Only a couple of lines of code differ. What exactly you change depends on your particular application and your particular needs. Hence no general purpose example.

Hi,

We are now testing activation logic for our subscription based app. I adapted your Java example to our case, where we also check expires custom field of the key. We should perform this check not only when the app launches, but also after user enters the key in activation prompt dialog.

Sometimes (80%) it doesn't activate after you click Activate button saying either key is expired (when it is not), or that system date is not valid on you computer (that is ugly user experience). But after you restart the app it works (date check passes).

Please take a look on source code

Adapted DocumentEditorView constructor...

 try{


      TurboActivate.SetTurboActivateFolder( "c:/LimeLM-Test/TurboActivate/" );      TurboActivate.SetPDetsLocation();      IsGenuineResult gr = TurboActivate.IsGenuine( 3, 7, true, false );      isActivated = gr == IsGenuineResult.Genuine ||   gr == IsGenuineResult.GenuineFeaturesChanged ||        gr == IsGenuineResult.InternetError;      if( gr == IsGenuineResult.InternetError ){      }    }    catch( Exception e ){      JOptionPane.showMessageDialog( null, "Failed to check if activated: " + e.getMessage() );      System.exit( 1 );    }


    ShowTrial( !isActivated );


    // if this app is activated then you can get a feature value    // See: http://wyday.com/limelm/help/license-features/    if(isActivated){


        try{


            if( !PKey.isKeyDateVaid() ){


                JOptionPane.showMessageDialog( null, "This product key is expired." );                //throw new Exception( "This product key is expired." );                //ShowTrial( true );                /*isActivated = false;                activateDeactivateMenuClick();*/


            }


        }        catch( Exception ex ){            JOptionPane.showMessageDialog( null, "Failed to check if expired: " + ex.getMessage() );            System.exit( 1 );        }


    }    else{        //activateDeactivateMenuClick();    }

PKey

private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {                                         		try		{			boolean existingKey = TurboActivate.IsProductKeyValid();			if ((existingKey && !TurboActivate.GetPKey().equals(txtPKey.getText())) || !existingKey)			{				// save the new key				if (!TurboActivate.CheckAndSavePKey(txtPKey.getText()))					throw new Exception("The product key is not valid.");			}


			// try to activate and close the form			final String extraData = getSystemExtraData();                        TurboActivate.Activate( extraData );


                        IsGenuineResult gr = TurboActivate.IsGenuine( 0, 1, true, false );                        if( gr == IsGenuineResult.Genuine ||  gr == IsGenuineResult.GenuineFeaturesChanged ){                          if( !isKeyDateVaid() ){                            throw new Exception( "This product key is expired." );                          }                        }                        else if( gr == IsGenuineResult.InternetError ){                            //TODO: give the user the option to retry the genuine checking immediately                            //For example a dialog box. In the dialog call IsGenuine() to retry immediately                            // or throw exception here                        }                        else{                            // ??? or throw exception here                        }


			doClose(RET_OK);		}		catch (Exception ex)		{			JOptionPane.showMessageDialog(null, ex.getMessage());		}    }      


public static boolean isKeyDateVaid() throws TurboActivateException,        UnsupportedEncodingException{


        final String expires_date = TurboActivate.GetFeatureValue( "expires" );        return TurboActivate.IsDateValid( expires_date, TurboActivate.TA_HAS_NOT_EXPIRED );


} 

So after you click Deactivate menu item in the Java example, and then try to activate again with the new key, or you use another key with valid expiration date it usually says "The product key is not valid.".

We now thinking about 3 options:1) Do not check for expiration date after Activate button click. But in this case user can continue to use the app with the same key and will be just prompted once in 3 days.2) Use timer to check expiration date after 2-5 minutes, because maybe LimeLM servers can't handle the check.3) Restart the app after Activate button click (if the key is valid) to pass the date check.

P.S. Also the java example doesn't honor Java Swing threading and performs network calls via EDT (UI) thread. This should be separated into SwingWorker and EDT threads.

We don't really have time to do free debugging work. We're in the middle of several big releases.

2) Use timer to check expiration date after 2-5 minutes, because maybe LimeLM servers can't handle the check.

Handle what check? You need to be more specific. Give a small snippet (2 to 3 lines of code) demonstrating a problem and we'll look at it.

So after you click Deactivate menu item in the Java example, and then try to activate again with the new key, or you use another key with valid expiration date it usually says "The product key is not valid.".

Add breakpoints to your code and figure out what's being passed. If there's an actual problem with TurboActivate then let us know. But I suspect it's a problem in your code.

P.S. Also the java example doesn't honor Java Swing threading and performs network calls via EDT (UI) thread. This should be separated into SwingWorker and EDT threads.

The function calls are synchronous by design. Some functions like IsGenuineEx() only intermittently "talk" to the server. In TurboActivate 3.5.x we're making the function calls threadsafe -- meaning if you want to make calls from other threads then that's your prerogative.

In your Java example code in PKey class okButtonActionPerformed() you call TurboActivate.Activate(); (we also pass extra data here). After that call you don't have pseudo code to check any custom field or if value is expired. You do have such pseudo code in the constructor of DocumentEditorView class. And at http://wyday.com/limelm/help/license-features/ you tell:

"For your app to get the latest field data call Activate(), IsGenuine(), or the IsGenuineEx() function. We recommend calling the IsGenuineEx() function because you can automate how often your app should check for new changes to the license fields on LimeLM."

To check if the date of the key is expired (custom field) I tried to call everything after TurboActivate.Activate() in okButtonActionPerformed(), but it doesn't work correctly, saying that the date of the key is expired, when it is not. Even when activation/deactivating with the same valid and not expired key. Only after you restart the app it passes the date check.

So can you provide source or pseudo code to check expiration date of the key when user presses Activate button? Without such check we can't prevent users from using the same key for unlimited time, when it should be limited on a monthly subscription, for example.

Like I said, I don't have time to dig through your code. Add breakpoints to your code to see what's wrong.

TurboActivate.Activate();


String expires = TurboActivate.GetFeatureValue("expires", null);


Boolean valid = TurboActivate.IsDateValid(expires, TurboActivate.TA_HAS_NOT_EXPIRED);

Add a breakpoint on every line to make sure the return values are what you expect.

It doesn't work. We fighting with your TurboActivation code for more than a month and it still doesn't work correctly. You said in change log: "v3.4.3 (October 4, 2013) - IsGenuineEx() was returning false positives for time tampering (TA_E_EXPIRED) under certain circumstances. ..."

But it looks like in our case it returning false negatives, saying either key is not valid or your system time is incorrect, when it is correct and key is not expired. Here is our activation pseudo code, when user clicks Activate button:

if( TurboActivate.IsActivated() ){ Deactivate();}

if( NEW_KEY_OR_EXISTING_NOT_VALID ){

final boolean NEW_KEY_IS_VALID = TurboActivate.CheckAndSavePKey( in_Key ); if( !NEW_KEY_IS_VALID ){ throw new Exception( "The product key is not valid." ); } else{ // do nothing }

}

TurboActivate.Activate( extraData );final IsGenuineResult genuineResult = TurboActivate.IsGenuine( 0, 1, true, false );

if( IS_GENUINE_OR_CHANGED ){ if( !isKeyDateVaid() ){ throw new Exception( "This product key is expired." ); }

}

Why it is not working usually, but only sometimes after several attempts??? We tried on 4-5 different PCs.

Why it is not working usually, but only sometimes after several attempts???

Again, you need to tell me exactly where it's failing. Which line? What's the error code? Add breakpoints to every line and slowly walk through the code.

TurboActivate.Activate( extraData );final IsGenuineResult genuineResult = TurboActivate.IsGenuine( 0, 1, true, false );

Why are you doing this? Activating contacts the server. You don't need to immediately follow that up by calling IsGenuine().

Our example application shows good usage of the TurboActivate API.

Also, why are you using the "final" keyword all over the place? Don't do that. In every case where you're using the final keyword, it's wrong (and likely where you're getting bad behavior). Just follow our example Java app. It gives you good API usage.

If you're still getting errors tell me exactly which line is failing and what the exception or error code states.

It is failing when trying to activate a valid key in

TurboActivate.Activate() throwing InvalidProductKeyException.

Ok, that tells you everything you need to know. Namely, that your product key hasn't been saved. So, call CheckAndSavePKey() before you call Activate. And if CheckAndSavePKey() throws an exception or returns false then you know the user entered and invalid product key.

Your example will be good when it will contain code checking feature value and expiration date after user clicks Activate button. Did you tested this case and on what PCs?

Before invalid key message (with current 3.4.3 message) it was system date invalid message, when we used version 3.4.2.

Using standard Java feature can broke TubroActivate??? I'm using final keyword because I'm not going to change the variable after assigning value to it. It is called Defensive programming. Read Code Complete.

Your example will be good when it will contain code checking feature value and expiration date after user clicks Activate button. Did you tested this case and on what PCs?

Yes, we test on many many computers and many many operating systems. To get a custom license field you use GetFeatureValue("custom field name", "default value if the field isn't present"). To verify a "date/time" use IsDateValid() or write your own code. If you're using IsDateValid() then your system timezone, time, and date must be correct. If TurboActivate detects time tampering then it says the date is not valid. That's by design.

Are you still getting an error? If so, what? Did you add a breakpoint after the CheckAndSavePKey() function? What did it return? Did it throw an exception?

Seems like using your code without final variables fixed the issue.

boolean existingKey = TurboActivate.IsProductKeyValid();if( ( existingKey && !TurboActivate.GetPKey().equals( in_Key ) ) || !existingKey ){ if( !TurboActivate.CheckAndSavePKey( in_Key ) ) throw new Exception( "The product key is not valid." );}

But it is very strange why using final is a problem.

final boolean EXISTING_KEY_VALID = TurboActivate.IsProductKeyValid();final boolean NEW_KEY_ENTERED = !TurboActivate.GetPKey().equals( in_Key );final boolean NEW_KEY_OR_EXISTING_NOT_VALID = ( EXISTING_KEY_VALID && NEW_KEY_ENTERED ) || !EXISTING_KEY_VALID;

if( NEW_KEY_OR_EXISTING_NOT_VALID ){

final boolean NEW_KEY_IS_VALID = TurboActivate.CheckAndSavePKey( in_Key ); if( !NEW_KEY_IS_VALID ){ throw new Exception( "The product key is not valid." ); }

}

Maybe JVM performs some optimizations or etc.