Stripe: Validating Publishable and Secret API Keys
Asked Answered
P

7

14

I'm builiding a web application that allows our users to sell tickets for music shows. In order to handle the payments between ticket buyers and show instigators, I use Stripe. Basically, the show instigator creates his show's page on my application, and the users can buy tickets for this show.

In order to create a show, the instigator fills in a form (Show's name, show's date, where the show will take place, what bands will be playing, etc.) This form also requires the show instigator to provide both his Publishable and Secret Stripe keys. My app uses both these tokens to retrieve credit cart information (on the client side) and process payments (on the server side).

The problem is, I want to make sure that show instigators provide valid and existing Stripe keys. I wouldn't want my users to stumble across payments errors because show instigators did not provide valid Stripe keys.

So, my question is: How can I verify that Publishable and Secret keys are valid and existing? What's the best strategy to achieve this? Thanks!

Polynesian answered 5/5, 2013 at 19:19 Comment(0)
O
2

I am not aware of any documented api call that can be made specifically to validate keys. Here is a suggestion you might try:

Require your partners to provide a valid credit card and inform them that in order to validate their Stripe keys, you will be making a $0.50 charge to their card that will be immediately refunded.

As part of your form validation, when both keys are given, submit a hidden form that contains all the data necessary to create a card token. You should be able to examine the response in your create card token response handler and determine if the publishable key is valid.

If you get a successful response back from the stripe server containing a card token, turn right around and submit a test charge for $0.50 (the minimum charge amount).

Make sure you're properly catching all the stripe exceptions. I believe with an invalid secret key, you should catch a Stripe_InvalidRequestError. If an exception is thrown you can report to the user.

If no errors are thrown, the charge will be made. Since you don't want to charge your partners, you'll want to capture the charge id from the stripe response and immediately refund the charge.

Orthotropous answered 6/5, 2013 at 17:54 Comment(1)
Simple, clean explanation. Thanks a lot pal! Though this solution is valid, I just discovered Strip Connect stripe.com/docs/connect. It is much more safer and simpler than managing the API keys yourself.Polynesian
A
12

Got it!

To validate your publishable keys you need to ask stripe for a new token using cURL. If the given key is invalid the response will contain an error message starting with "Invalid API Key provided".

Here's an example written in PHP:

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://api.stripe.com/v1/tokens");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "card[number]=4242424242424242&card[exp_month]=12&card[exp_year]=2017&card[cvc]=123");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_USERPWD, $publishableKey . ":");

$response = json_decode(curl_exec($ch),true);

if( curl_errno($ch) ){
    echo 'Error:' . curl_error($ch);
}
curl_close ($ch);

if(substr($response["error"]["message"],0, 24 ) == "Invalid API Key provided"){
    echo "Invalid API Key provided";
}

Same idea for validating your secret keys.

Aunt answered 12/3, 2014 at 22:53 Comment(5)
When I hit that endpoint I get a message Unrecognized request URL (GET: /v1/tokens). Please see https://stripe.com/docs or we can help at https://support.stripe.com/."Centistere
@AdamParkin /v1/tokens only accepts POST, not GET requests. You need to specify curl_setopt($ch, CURLOPT_POST, true) too.Spunk
Not working with post $url = 'api.stripe.com/v1/tokens?key='.$publishableKey; "error": { "type": "invalid_request_error", "message": "You must supply either a card, customer, pii data, or bank account to create a token." }Grip
This answer is only valid for test mode. In production it will end with the following: "error": { "code": "card_declined", "decline_code": "live_mode_test_card" ... }Nephrolith
I used this code to check both the public and private key and it works correctly, I removed the statement "CURLOPT_POSTFIELDS" and in this way it works for both the "test" and the "live" environmentNoctambulous
D
7

Validating the secret key is easy, simply calling the Stripe API with any command on the server side.

But for the public key... I found a way with Stripe.js :

let stripe = Stripe( <public key to test> );
setTimeout( ()=>{
    stripe.createToken('pii', {personal_id_number: 'test'})
        .then( result =>{
            if( result.token )
               // public key is valid :o)
            else 
              // nope !
        })
}, 300 )

Note the timeout before calling stripe.createToken(). If you don't do it, the promise returned by createToken() will never come back.

UPDATE: Just received a confirmation from Stripe; this it is a valid and acceptable method.

Donatelli answered 2/4, 2020 at 21:30 Comment(1)
do note of course you need to put the include before this code ex. <script src="js.stripe.com/v3/"></script>Sev
O
2

I am not aware of any documented api call that can be made specifically to validate keys. Here is a suggestion you might try:

Require your partners to provide a valid credit card and inform them that in order to validate their Stripe keys, you will be making a $0.50 charge to their card that will be immediately refunded.

As part of your form validation, when both keys are given, submit a hidden form that contains all the data necessary to create a card token. You should be able to examine the response in your create card token response handler and determine if the publishable key is valid.

If you get a successful response back from the stripe server containing a card token, turn right around and submit a test charge for $0.50 (the minimum charge amount).

Make sure you're properly catching all the stripe exceptions. I believe with an invalid secret key, you should catch a Stripe_InvalidRequestError. If an exception is thrown you can report to the user.

If no errors are thrown, the charge will be made. Since you don't want to charge your partners, you'll want to capture the charge id from the stripe response and immediately refund the charge.

Orthotropous answered 6/5, 2013 at 17:54 Comment(1)
Simple, clean explanation. Thanks a lot pal! Though this solution is valid, I just discovered Strip Connect stripe.com/docs/connect. It is much more safer and simpler than managing the API keys yourself.Polynesian
M
1

I have been trying to figure out a good way validate provided Stripe API keys also. I figured out a way to validate the secret key without having to make a purchase by creating a test customer. I am still looking into validating the publishable key but I thought this might help someone.

Here is a PHP example:

try {
    \Stripe\Stripe::setApiKey($secret_key);

    // create a test customer to see if the provided secret key is valid
    $response = \Stripe\Customer::create(["description" => "Test Customer - Validate Secret Key"]); 

    return true;
}
// error will be thrown when provided secret key is not valid
catch (\Stripe\Error\InvalidRequest $e) {
    // Invalid parameters were supplied to Stripe's API
    $body = $e->getJsonBody();
    $err  = $body['error'];

    $messages = array();
    $messages[] = 'Status is: ' . $e->getHttpStatus();
    $messages[] = 'Type is: ' . $err['type'];
    $messages[] = 'Code is: ' . $err['code'];
    $messages[] = 'Decline Code is: ' . $err['decline_code'];
    $messages[] = 'Message: ' . $err['message'];

    return false;
}
catch (\Stripe\Error\Authentication $e) {
    // Authentication with Stripe's API failed
    // (maybe you changed API keys recently)
    $body = $e->getJsonBody();
    $err  = $body['error'];

    $messages = array();
    $messages[] = 'Status is: ' . $e->getHttpStatus();
    $messages[] = 'Type is: ' . $err['type'];
    $messages[] = 'Code is: ' . $err['code'];
    $messages[] = 'Decline Code is: ' . $err['decline_code'];
    $messages[] = 'Message: ' . $err['message'];

    return false;
}
catch (\Stripe\Error\ApiConnection $e) {
    // Network communication with Stripe failed
    $body = $e->getJsonBody();
    $err  = $body['error'];

    $messages = array();
    $messages[] = 'Status is: ' . $e->getHttpStatus();
    $messages[] = 'Type is: ' . $err['type'];
    $messages[] = 'Code is: ' . $err['code'];
    $messages[] = 'Decline Code is: ' . $err['decline_code'];
    $messages[] = 'Message: ' . $err['message'];

    return false;
}
catch (\Stripe\Error\Base $e) {
    // Display a very generic error to the user, and maybe send
    // yourself an email
    $body = $e->getJsonBody();
    $err  = $body['error'];

    $messages = array();
    $messages[] = 'Status is: ' . $e->getHttpStatus();
    $messages[] = 'Type is: ' . $err['type'];
    $messages[] = 'Code is: ' . $err['code'];
    $messages[] = 'Decline Code is: ' . $err['decline_code'];
    $messages[] = 'Message: ' . $err['message'];

    return false;
}
catch (Exception $e) {
    // Something else happened, completely unrelated to Stripe
    $body = $e->getJsonBody();
    $err  = $body['error'];

    $messages = array();
    $messages[] = 'Status is: ' . $e->getHttpStatus();
    $messages[] = 'Type is: ' . $err['type'];
    $messages[] = 'Code is: ' . $err['code'];
    $messages[] = 'Decline Code is: ' . $err['decline_code'];
    $messages[] = 'Message: ' . $err['message'];

    return false;
}       
Musteline answered 4/1, 2019 at 19:57 Comment(1)
You could also just get the balance instead of creating an actual object. This keeps your stripe account clean.Aubrey
A
0

this is working client side validation example with js

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

<script>
    Stripe.setPublishableKey('{{FILL_THIS_WITH_YOURS}}');
    Stripe.createToken({}, function(status, response) {
        if (status == 401) {
            //invalid public key
        } else {
            //valid public key
        }
    });
</script>
Annatto answered 17/6, 2020 at 20:12 Comment(0)
R
0

here is the easiest solution I found with Stripe JS v3 for validating the public key.

Then if the key is valid, you can send your form with ajax and validate the secret key server side with the same system.

$('body').on('submit', '.stripe_validate_keys', function(e){
        e.preventDefault();

        //public key submitted by your form
        var public_key = $('.stripe_validate_keys').find('input[name="ba_stripe_public"]').val();

        
        stripe = Stripe( public_key, {
            betas: ['payment_intent_beta_3']
        });

        //Trying to retrieve a customer who don't exist
        //You have to pass only the prefix
        stripe.retrieveSource({
            id: 'src_',
            client_secret: 'src_client_secret_',
        })
        .then(function(result) {
            var res = result.error.message;
            

            var test = res.search( 'Invalid API Key' );
            

            if( test === 0 ){
                //Key invalid
            } else {
                //Now you can submit your form for validate secret key server side
            }
        });
    });
Reticule answered 13/3, 2021 at 11:59 Comment(0)
H
0

I'm a bit late to the party, but I thought I would share my implementations in the hope that someone will find them useful.

Publishable Key

Here's my implementation for validating a publishable key in a Vue component, extended from this vanilla JS answer:

import {loadStripe} from '@stripe/stripe-js';
...
/**
 * Validate the public key
 * 
 * @param value
 * @returns {Promise<string>}
 */
async validatePublicKey(value)
{
    //must start with pk_
    if (!/^pk_/.test(value))
        return 'Public key must start with pk_';
    //Test that Stripe accepts the key
    //Init Stripe JS
    let stripe = await loadStripe(value);
    let {token, error} = await stripe.createToken('pii', {personal_id_number: 'test'});
    if (error)
        return error?.message ?? 'Invalid public key';
}

As others have mentioned, Stripe have said this is an acceptable method of validating a publishable key.

Secret Key

You can use the same method on your backend to validate the secret key. Below is a sample funcion in PHP:

/**
 * Check if a given string is a valid stripe secret key by attempting to make a request to the stripe API
 * 
 * @param string $key
 * @return bool
 * @throws \Stripe\Exception\ApiErrorException
 */
function validateSecretKey(string $key): bool
{
    if (!str_starts_with($key, 'sk_'))
        return false;
    //Instantiate the stripe client with the key that needs validating
    $client = new \Stripe\StripeClient([
        "api_key" => $key
    ]);
        
    try
    {
        //Attempt to create a pii token 
        $client->tokens->create(["pii" => ["personal_id_number" => "test"]]);
    }
    catch (\Stripe\Exception\AuthenticationException)
    {
        //If the key is invalid, stripe will throw an authentication exception
        return false;
    }
        
    return true;
}
Hhd answered 9/8 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.