2

I have been working for days trying to figure out how to charge a card and save the card to a customer with Stripe using Swift, with Parse.com Cloud Code as the backend. I integrated the Stripe pod with my project successfully, and I have a token that is created, and prints out in the console to verify its existence. BUT I cannot do anything with it! I have searched everywhere for answers, and cannot figure out why I keep getting error. I think it has to do with the parameters that I am trying to feed to the Cloud Code, but I am unsure. I have read the docs for both Cloud Code and Stripe, and it was to no avail. This is my PaymentViewController.swift:

import UIKit
import Stripe
import PaymentKit
import Parse
import Bolts




class PaymentViewController: UIViewController, PTKViewDelegate {


@IBOutlet weak var saveBtn: UIButton!

var paymentView: PTKView = PTKView()

override func viewDidLoad() {
    super.viewDidLoad()



    var view : PTKView = PTKView(frame: CGRectMake(15,20,290,55))

    paymentView = view
    paymentView.delegate = self;
    self.view.addSubview(self.paymentView)

    saveBtn.enabled = false



}

func paymentView(view: PTKView!, withCard card: PTKCard!, isValid valid: Bool) {
    if (valid) {
        saveBtn.enabled = true
    } else {
        saveBtn.enabled = false
    }
}



override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}





@IBAction func save(sender: AnyObject) {

    var card: STPCard = STPCard()
    card.number = self.paymentView.card.number
    card.expMonth = self.paymentView.card.expMonth
    card.expYear = self.paymentView.card.expYear
    card.cvc = self.paymentView.card.cvc

    STPAPIClient.sharedClient().createTokenWithCard(card, completion: { (tokenId: STPToken?, error: NSError?) -> Void in
        if (error != nil) {
            println(error)
            println("what the..")
        } else {
            println(tokenId)


            PFCloud.callFunctionInBackground("hello", withParameters: nil) {
                (response: AnyObject?, error: NSError?) -> Void in
                let responseString = response as? String
                println(responseString)
            }




            PFCloud.callFunctionInBackground("createCharge", withParameters: nil, block: { (success: AnyObject?, error: NSError?) -> Void in
                if error != nil {
                    println("error")
                }
            })








        }
    })



}







@IBAction func cancel(sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: nil)
}






}

I have added the "Hello World" example to see if the Cloud Code was set up correctly, and that callFunction does work. My Cloud Code is:

var Stripe = require('stripe');
Stripe.initialize('My_Secret_Key');

Parse.Cloud.define("hello", function(request, response) {
               response.success("Hello world!");
               });

Parse.Cloud.define("createCharge", function(request, response) {
               Stripe.Charges.create({
                                     amount: 100 * 10, // $10 expressed in cents
                                     currency: "usd",
                                     card: "tok_3TnIVhEv9P24T0"
                                     },{
                                     success: function(httpResponse) {
                                     response.success("Purchase made!");
                                     },
                                     error: function(httpResponse) {
                                     response.error("Uh oh, something went wrong");
                                     }
                                     });


               });

Any help would be truly appreciated!! I have been working tirelessly to figure this out! The console prints out

Uh oh, something went wrong (Code: 141, Version: 1.7.2)
Matthew Arkin
  • 4,460
  • 2
  • 27
  • 28

2 Answers2

0

The error you are seeing Uh oh, something went wrong (Code: 141, Version: 1.7.2) means that your createCharge Parse function returned an error. You may want to log the value of httpResponse to find the exact error from Stripe.

Looking at your code the error is most likely: Invalid Request Error: Cannot use token tok_3TnIVhEv9P24T0 more than once. You can confirm this in the Logs section of your Stripe Dashboard.

I see that you print the token, println(tokenId), you're also going to want to send that to your Parse function and set card equal to the value of the token you just created.

Matthew Arkin
  • 4,460
  • 2
  • 27
  • 28
  • Hi, thanks for the response. I tried what you said, and I got this response [Error]: Error: Can't call success/error multiple times at Object.error (:106:9) at Object.Stripe.Charges.create.error (main.js:19:51) at Object.Parse.Cloud.httpRequest.error (stripe.js:107:19) at Object. (:578:19) (Code: 141, Version: 1.7.4) I am not sure why this is happening still. Any thoughts? – Datt Monahue Jun 08 '15 at 00:53
  • huh, thats a weird error, it would indicate your're trying to call response.success / error more than once. How much has you changed your cloud function? – Matthew Arkin Jun 08 '15 at 06:31
  • So, I managed to figure out how to get the token to stripe. I added var token = token!.tokenId and I passed in ["token" : token] as the parameter. However, I am now trying to create the customer, and save it with parse objectId, and I am getting the same error as above, when trying to pass in the customer into the createCharge parameter. Would you like me to post my code to show you? – Datt Monahue Jun 08 '15 at 18:00
  • Sure, so you're creating a charge and creating a customer? – Matthew Arkin Jun 08 '15 at 21:22
  • Yeah, I want to create the customer, and then create a charge based on the customer, so that the user does not have to input their CC info each time. I will post it below – Datt Monahue Jun 08 '15 at 22:32
0
class PaymentViewController: UIViewController, PTKViewDelegate {


@IBOutlet weak var saveBtn: UIButton!

var paymentView: PTKView = PTKView()

override func viewDidLoad() {
    super.viewDidLoad()



    var view : PTKView = PTKView(frame: CGRectMake(15,20,290,55))

    paymentView = view
    paymentView.delegate = self;
    self.view.addSubview(self.paymentView)

    saveBtn.enabled = false



}

func paymentView(view: PTKView!, withCard card: PTKCard!, isValid valid: Bool) {
    if (valid) {
        saveBtn.enabled = true
    } else {
        saveBtn.enabled = false
    }
}



override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}





@IBAction func save(sender: AnyObject) {

    var card: STPCard = STPCard()
    card.number = self.paymentView.card.number
    card.expMonth = self.paymentView.card.expMonth
    card.expYear = self.paymentView.card.expYear
    card.cvc = self.paymentView.card.cvc

    STPAPIClient.sharedClient().createTokenWithCard(card, completion: { (token: STPToken?, error: NSError?) -> Void in
        if (error != nil) {
            println(error)
            println("not working")
        } else {
            //println(tokenId)
            var coin = token!.tokenId


            PFCloud.callFunctionInBackground("hello", withParameters: nil) {
                (response: AnyObject?, error: NSError?) -> Void in
                let responseString = response as? String
                println(responseString)
            }

            var name = PFUser.currentUser()?.username as String!
            var customer = PFUser.currentUser()?.objectId as String!


            PFCloud.callFunctionInBackground("createCustomer", withParameters: ["coin" : coin, "name": name, "customer": customer], block: { (success: AnyObject?, error: NSError?) -> Void in
                if error != nil {
                    println("create customer not working")
                }




            })

            var customerId = customer!

            PFCloud.callFunctionInBackground("createCharge", withParameters: ["customerId" : customerId], block: { (success: AnyObject?, error: NSError?) -> Void in
                if error != nil {
                    println("not working")
                }
            })








        }
    })



}



@IBAction func cancel(sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: nil)
}

}

And My updated cloud code is here:

var Stripe = require('stripe');
Stripe.initialize('sk_test_xxxxxxxxxxxxxx');

Parse.Cloud.define("hello", function(request, response) {
               response.success("Hello world!");
               });



Parse.Cloud.define("createCustomer", function(request, response) {
                                  Stripe.Customers.create({
                                                         card:     request.params.coin,
                                                          account_balance: -10*100,
                                                          metadata: {
                                                          name: request.params.name,
                                                          customer: request.params.customer, // e.g PFUser object ID
                                                          }

                                                         }, {
                                                         success: function(customer) {
                                                         response.success(customer.id);
                                                         },
                                                         error: function(error) {
                                                         response.error("Error:" +error);
                                                         }
                                                         })
                                  });

 

Parse.Cloud.define("createCharge", function(request, response) {
               Stripe.Charges.create({
                                     amount: 100 * 10, // $10 expressed in cents
                                     currency: "usd",
                                     //card: request.params.coin
                                     customer: request.params.customerId
                                     },{
                                     success: function(httpResponse) {
                                     response.success("Purchase made!");
                                     },
                                     error: function(httpResponse) {
                                     response.error(httpResponse)
                                     response.error("Uh oh, something went wrong");
                                     }
                                     });


               });

Ultimately I want to have the create customer in a different viewController.swift file and charge on another section of the app, but for right now I am testing it with them in the same PaymentViewController.swift file

  • Well now you're doing a whole bunch of funky stuff. First there is no need to set an account balance on the customer. That's only useful for subscriptions. Is the customer being created successfully? – Matthew Arkin Jun 08 '15 at 22:49
  • haha yeah, sorry, I added an account balance and stuff in order test to see if I was doing things correctly, and if it would show up on my Stripe Dashboard, which it did. The customer is being created successfully, but I can't seem to get that customer to be charged. – Datt Monahue Jun 08 '15 at 23:09
  • In the Stripe dashboard you should be able to go to logs, do you see any 4XX (where XX is either 00 or 02) to /v1/charges? – Matthew Arkin Jun 08 '15 at 23:33
  • no, they are just 200 POST/v1/customers. No 4XX or v1/charges – Datt Monahue Jun 08 '15 at 23:39