3

I need to make a http post request using the following instructions:

curl https://api.stripe.com/v1/tokens \
   -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
   -d "bank_account[country]=US" \
   -d "bank_account[routing_number]=110000000" \
   -d "bank_account[account_number]=000123456789"

I have no idea how to go from curl to NSUrlrequest, especially the -u (user?) part. The stripe SDK has left this out from their SDK and has no example on their site.

Thanks

Mehdi
  • 772
  • 7
  • 16

2 Answers2

2

EDIT:I created another answer specifically for getting a token for a bank_account. This answer was for generally how to make a call using parse's back end using an example of creating a recipient.

The stipe documentation is a little off here, the call for creating a bank_account token is actually made using the publishable key directly from the app. Make sure not to use your secret key in the iOS app itself. Only your public key should be used via:

[Stripe setDefaultPublishableKey:@"pk_test_your_test_key_here"];

You need to use a web back end to implement the complete functionality of the stripe payment system. The ios sdk they include only gets you as far as getting a token from a credit card. The web back end is there you would implement the secret key. I use parse.com as my backend for stripe but many implement their own.

Stripe ios Tutorial ios stripe backend

Below is a simple httpRequest cloud code that can perform most stripe tasks. Feed it a method, prefix, suffix, postfix, and then the parameters of the request. I'm not saying this is the best way to implement stripe's httpRequests, but it covers the bases for you to start working on it. The code below is tested, and it works, I created a john doe recipient in my stripe test sandbox.

Parse Cloud code:

var Stripe = require('stripe');
var STRIPE_SECRET_KEY = 'sk_test_yoursecretkeyhere';
var STRIPE_API_BASE_URL = 'api.stripe.com/v1/'
Stripe.initialize(STRIPE_SECRET_KEY);

Parse.Cloud.define("stripeHTTPRequest", function(request, response) 
{
    //check for suffix, and postfix
    var suffix = "";
    if (!isEmpty(request.params["suffix"])) {
        suffix = '/'+request.params['suffix'];
    }
    var postfix = "";
    if (!isEmpty(request.params["postfix"])) {
        postfix = '/'+request.params['postfix'];
    }   

    Parse.Cloud.httpRequest({
            method: request.params["method"],
            url: 'https://' + STRIPE_SECRET_KEY + ':@' + STRIPE_API_BASE_URL + request.params["prefix"] + suffix + postfix,
            params:request.params["parameters"],
            success: function(httpResponse) {
            response.success(httpResponse.text);
            },
            error: function(httpResponse) {
            response.error('Request failed with response code' + httpResponse.status);
            }
    });
});

function isEmpty(obj) {

    // null and undefined are "empty"
    if (obj == null) return true;

    // Assume if it has a length property with a non-zero value
    // that that property is correct.
    if (obj.length > 0)    return false;
    if (obj.length === 0)  return true;

    // Otherwise, does it have any properties of its own?
    // Note that this doesn't handle
    // toString and valueOf enumeration bugs in IE < 9
    for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) return false;
    }

    return true;
}

So when it comes to creating a recipient, you would feed it a method of "POST", and a prefix of "recipients", and leave the suffix, and postfix empty. This would generate a url of such:

https://sk_test_yoursecretkeyhere:@api.stripe.com/v1/recipients

In addition to the method & pre/suf/postfix you would need to feed it parameters. You can do this by sending a dictionary of keyed objects. Using Stripe's documentation, lets create a recipient named john doe:

   -d "name=John Doe" \
   -d type=individual \
   -d tax_id=000000000 \
   -d "email=test@example.com" \
   -d "description=Recipient for John Doe"

Here is the iOS call of the cloud code using the John Doe example. I have implemented a general method the you pass the method, pre/suf/postfix's and parameters. I then create many additional methods to handle the specific stripe calls, like creating a recipient for example.

ViewController.m

-(void)createJohnDoe
{
    NSDictionary *parameters = @{@"name":@"John Doe",
                                 @"type":@"individual",
                                 @"tax_id":@"000000000",
                                 @"email":@"test@example.com",
                                 @"description":@"Recipient for John Doe"
                                 };
    [ELStripe executeStripeCloudCodeWithMethod:@"POST" prefix:@"recipients" suffix:nil postfix:nil parameters:parameters completionHandler:^(id jsonObject, NSError *error) {
        //jsonObject will be a dictionary that would need be parsed into your recipient object
        NSLog(@"jsonObject:%@",jsonObject);
    }];
}

ELStripe.m

   //Completion Handler Definition.
   typedef void (^ELStripeCompletionBlock)(id jsonObject, NSError *error);

   +(void)executeStripeCloudCodeWithMethod:(NSString *)method prefix:(NSString *)prefix suffix:(NSString *)suffix postfix:(NSString *)postfix parameters:(NSDictionary *)parameters completionHandler:(ELStripeCompletionBlock)handler
{

    [PFCloud callFunctionInBackground:@"stripeHTTPRequest" withParameters:@{@"method":method, @"prefix":prefix?prefix:@"", @"suffix":suffix?suffix:@"", @"postfix":postfix?postfix:@"", @"parameters":parameters} block:^(id object, NSError *error) {
        id jsonObject;
        if (!error) {
            NSError *jsonError = nil;
            jsonObject = [NSJSONSerialization JSONObjectWithData:[object dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&jsonError];
        }
        handler(jsonObject,error);
    }];
}
hybrdthry911
  • 1,259
  • 7
  • 12
  • Hi, and thanks for the detailed response. The stripe SDK does not support creating Recipients. The first step is to create a token for a bank account or debit card, then from there you can attach a recipient from cloud code (Parse) to that tokenized card. The tokenization of a card for charging is different than the one used for a recipient, as you can included tax_id and other details to that account. Right now my only solution is to only support bank accounts and not debit cards, and to send everything to cloud code, and from there create a token. Ur help would be great.BTW that id is fake – Mehdi Sep 02 '14 at 03:05
  • Parse's stripe SDK does leave a bit to be desired I'll admit, but everything can be accomplished using a "Parse.Cloud.httpRequest" inside a cloud code function. In some ways it can actually be easier. I'll edit my answer and add a simple cloud code function that can be utilized. – hybrdthry911 Sep 02 '14 at 03:42
  • The issue there is that you can't send credit card info to cloud code (PCI Compliance issue), only bank account info can be sent to cloud code. I do HTTP request in cloud code for the bank account tokenization. But to send the credit card it needs to go directly from the client to stripe. – Mehdi Sep 02 '14 at 03:47
  • Are you looking for help generating the actual bank_account token or the recipient? I never send credit card numbers to my cloud code, only the tokenId's, and I don't deal with bank accounts but would obviously treat them the same as well. – hybrdthry911 Sep 02 '14 at 04:17
  • Took a look at your main post again and I did in fact realize you were specifically looking for the generation of a bank account token, not the stripe calls in general. With that said I'll show you how I would accomplish this in another answer. – hybrdthry911 Sep 02 '14 at 04:28
  • Yes, I am looking for the generation of bank account / credit card account so that I can attach it to a recipient. It would be supper great if you show that. Thank you so much. – Mehdi Sep 03 '14 at 04:57
  • Check my other answer. – hybrdthry911 Sep 03 '14 at 05:23
1

STPCard has a method -(NSData *)formEncode that stripe.m uses to populate the body of the nsurlrequest. I used their format to create my own encode with bank account information instead of the card.

Stripe's token creation with a card:

Stripe.m

+ (void)createTokenWithCard:(STPCard *)card publishableKey:(NSString *)publishableKey operationQueue:(NSOperationQueue *)queue completion:(STPCompletionBlock)handler{
    if (card == nil) {
        [NSException raise:@"RequiredParameter" format:@"'card' is required to create a token"];
    }

    if (handler == nil) {
        [NSException raise:@"RequiredParameter" format:@"'handler' is required to use the token that is created"];
    }

    [self validateKey:publishableKey];

    NSURL *url = [self apiURLWithPublishableKey:publishableKey];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [card formEncode];
    [request setValue:[self JSONStringForObject:[self stripeUserAgentDetails]] forHTTPHeaderField:@"X-Stripe-User-Agent"];

    [[[STPAPIConnection alloc] initWithRequest:request] runOnOperationQueue:queue
                                                                 completion:^(NSURLResponse *response, NSData *body, NSError *requestError) {
                                                                     [self handleTokenResponse:response body:body error:requestError completion:handler];
                                                                 }];
}

STPCard.m

- (NSData *)formEncode
{
    NSMutableDictionary *params = [NSMutableDictionary dictionary];

    if (_number) params[@"number"] = _number;
    if (_cvc) params[@"cvc"] = _cvc;
    if (_name) params[@"name"] = _name;
    if (_addressLine1) params[@"address_line1"] = _addressLine1;
    if (_addressLine2) params[@"address_line2"] = _addressLine2;
    if (_addressCity) params[@"address_city"] = _addressCity;
    if (_addressState) params[@"address_state"] = _addressState;
    if (_addressZip) params[@"address_zip"] = _addressZip;
    if (_addressCountry) params[@"address_country"] = _addressCountry;
    if (_expMonth) params[@"exp_month"] = [NSString stringWithFormat:@"%lu", (unsigned long) _expMonth];
    if (_expYear) params[@"exp_year"] = [NSString stringWithFormat:@"%lu", (unsigned long) _expYear];

    NSMutableArray *parts = [NSMutableArray array];

    [params enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
        if (val != [NSNull null]) {
            [parts addObject:[NSString stringWithFormat:@"card[%@]=%@", key, [STPUtils stringByURLEncoding:val]]];
        }
    }];

    return [[parts componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding];
}

My barebones bank_account creation:

BankAccount.h

#import <Foundation/Foundation.h>

@interface BankAccount : NSObject
 @property (strong, nonatomic) NSString *country, *routingNumber, *accountNumber;
- (NSData *)bankAccountEncode;
@end

BankAccount.m

#import "BankAccount.h"
#import "STPUtils.h"

@implementation BankAccount

//Similar to STPCard's formEncode
- (NSData *)bankAccountEncode
{
    NSMutableDictionary *params = [NSMutableDictionary dictionary];

    if (_country) params[@"country"] = @"US";
    if (_routingNumber) params[@"routing_number"] = _routingNumber;
    if (_accountNumber) params[@"account_number"] = _accountNumber;


    NSMutableArray *parts = [NSMutableArray array];

    [params enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop) {
        if (val != [NSNull null]) {
            [parts addObject:[NSString stringWithFormat:@"bank_account[%@]=%@", key, [STPUtils stringByURLEncoding:val]]];
        }
    }];

    return [[parts componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding];
}

@end

My Stripe.m Additions. Notice I use a BankAccount object, and then use it's - (NSData *)bankAccountEncode instead of STPCard's formEncode.

+ (void)createTokenWithBankAccount:(BankAccount *)bankAccount completion:(STPCompletionBlock)handler{
    [self createTokenWithBankAccount:bankAccount publishableKey:[self defaultPublishableKey] operationQueue:[NSOperationQueue mainQueue] completion:handler];
}

+ (void)createTokenWithBankAccount:(BankAccount *)bankAccount publishableKey:(NSString *)publishableKey operationQueue:(NSOperationQueue *)queue completion:(STPCompletionBlock)handler{
    if (bankAccount == nil) {
        [NSException raise:@"RequiredParameter" format:@"'card' is required to create a token"];
    }

    if (handler == nil) {
        [NSException raise:@"RequiredParameter" format:@"'handler' is required to use the token that is created"];
    }

    [self validateKey:publishableKey];

    NSURL *url = [self apiURLWithPublishableKey:publishableKey];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    request.HTTPMethod = @"POST";

    //Changed this here to get the bank account encode instead of the credit card encode
    request.HTTPBody = [bankAccount bankAccountEncode];
    [request setValue:[self JSONStringForObject:[self stripeUserAgentDetails]] forHTTPHeaderField:@"X-Stripe-User-Agent"];

    [[[STPAPIConnection alloc] initWithRequest:request] runOnOperationQueue:queue
                                                                 completion:^(NSURLResponse *response, NSData *body, NSError *requestError) {
                                                                     [self handleTokenResponse:response body:body error:requestError completion:handler];
                                                                 }];
}
hybrdthry911
  • 1,259
  • 7
  • 12