I want to add two stripe keys to my App delegate field in Swift iOS. I have two stripe accounts and based on conditions I want to do credit/debit card payments. Thanks in advance.
For what I have done is instead of declaring the string keys in App delegate I am declaring the keys in the class itself where I am creating the token for payments like below. I have two classes in each class I am using each key.
#IBAction func submitCard(_ sender: AnyObject) {
let cardParams = paymentTextField.cardParams
Stripe.setDefaultPublishableKey("pk_test_1234567899875gh")// example key
// It takes the card details and creates the token here.
STPAPIClient.shared().createToken(withCard: cardParams) { token, error in
guard token != nil else {
print("Error creating token: %#", error!.localizedDescription);
return
}
print(token)
}
}
Is this write? or do I need to change the code?
Note: This is not about the environment, but two different stripe accounts.
You can simply create multiple STPAPIClients using different keys and then use them as needed, instead of using the shared instance.
https://stripe.dev/stripe-ios/docs/Classes/STPAPIClient.html#/c:objc(cs)STPAPIClient(im)initWithPublishableKey:
let clientA = STPAPIClient(publishableKey: "pk_123")
let clientB = STPAPIClient(publishableKey: "pk_456")
if usingClientA {
clientA.createToken(withCard: cardParams) { ... }
} else if usingClientB {
clientB.createToken(withCard: cardParams) { ... }
}
Also as a general point, you might want to have the public key get returned from an endpoint on your backend server rather than hardcoding into the app, as that makes it easier if you need to change the Stripe account used without going through app review.
Ideally you should use one Stripe account only. And the way you set your publishableKey in your AppDelegate depends on your current environment (i.e. development / production), like so:
let key = isProduction ? "pk_live_TYooMQauvdEDq54NiTphI7jx" : "pk_test_TYooMQauvdEDq54NiTphI7jx"
Stripe.setDefaultPublishableKey(key)
Should you need to set a new key based on "conditions" as what you've mentioned, later on, just set it with the function setDefaultPublishableKey.
You can use this condition in AppDelegate:
#if DEBUG
Stripe.setDefaultPublishableKey(testKey)
#else
Stripe.setDefaultPublishableKey(liveKey)
#endif
Result: if you'll use a debug environment, then Stripe will get the test key. In the case of archive and deploy to TestFlight, for example, Stripe will get live keys.
Related
I am trying to create an ios app which safely collects a user's bank account information (with the intention of paying the user) using Stripe. Stripe recommends that I collect the bank information in an instance of STPBankAccountParams. This is not too bad:
var bankAccount = STPBankAccountParams()
bankAccount.routingNumber = routingNumber
bankAccount.accountNumber = accountNumber
...
Stripe then recommends that you tokenize the bankAccount for security purposes before sending to backend. They recommend you use this function:
func createToken(withBankAccount bankAccount: STPBankAccountParams, completion: STPTokenCompletionBlock? = nil)
The documentation on this function is a bit sparse: Docs
I am not sure how to run this function in my code. I want to use this function and get the token, but I lack understanding on how to do that in code. I want to run something like:
token = createToken(withBankAccount: bankAccount)
But of course that and other things I have tried have not worked yet. Does anyone have experience running the createTokenWithBankAccount() function in Stripe?
MadProgrammer's answer was very close, but did not actually work. I did talk with a representative from Stripe. For reference, he recommended the following code, which seems to work:
STPAPIClient.shared().createToken(withBankAccount: bankAccount) { (token, error) in
if let error = error {
print(error)
}
else {
print(token)
}
}
STPTokenCompletionBlock is closure (or callback), when the function completes (what ever task it was doing), it will call this block, passing you a STPToken or a Error. You would use something like
createToken(withBankAccount: bankAccount) { (token, error) in
// your code to check the token and error
}
This is a pretty common pattern and I suggest you take a look at something like Swift Programming Language: Closures
Just FYI I posted this question originally in the AWS AppSync forum (in case in the future AWS answers it).
I have been trying to make a simple Posts app like the one in the docs but I have found no documentation or guides that handle multiple subscriptions in one view controller.
Three mutations: onCreatePost, onUpdatePost, onDeletePost
(and of course three subscriptions to those mutations)
In Xcode, I have three functions called during viewDidLoad(): subscribeToNewPosts(), subscribeToUpdatedPosts(), subscribeToDeletedPosts()
Each subscription function works and creates a subscription with the correct functionality and updates the table view accordingly if used alone. But, if called one after the other, only the last subscription will actually receive data and update the table view. I put a breakpoint to check out topicSubscribersDictionary in AppSyncMQTTClient.swift after subscribing to all three mutations
func startNewSubscription(subscriptionInfo: AWSSubscriptionInfo) {
var topicQueue = [String]()
let mqttClient = MQTTClient<AnyObject, AnyObject>()
mqttClient.clientDelegate = self
for topic in subscriptionInfo.topics {
if topicSubscribersDictionary[topic] != nil {
// if the client wants subscriptions and is allowed we add it to list of subscribe
topicQueue.append(topic)
}
}
mqttClients.append(mqttClient)
mqttClientsWithTopics[mqttClient] = topicQueue
mqttClient.connect(withClientId: subscriptionInfo.clientId, toHost: subscriptionInfo.url, statusCallback: nil)
}
and all three subscriptions are in fact in the dictionary...
Do I need multiple instances of appSyncClient, one for each subscription? Is it a problem with the schema design?
schema.graphql
schema.json
mutations.graphql
queries.graphql
subscriptions.graphql
Example use case: simple chat app. New conversation started = OnCreatePostSubscription; new incoming message in that conversation = OnUpdatePostSubscription
Are you using API Key for authorization in AppSync? If you are using API Key only one subscription is supported by the SDK at this point. Could you switch to IAM (Cognito Identity) or Cognito UserPools based auth and see if multiple subscriptions work for you?
I managed to have several subscriptions working with API Key by replacing the call startSubscriptions to startNewSubscription inside AWSAppSyncSubscriptionWatcher
if let subscriptionInfo = subscriptionResult.subscrptionInfo {
self.subscriptionTopic = subscriptionResult.newTopics
self.client?.addWatcher(watcher: self, topics: subscriptionResult.newTopics!, identifier: self.uniqueIdentifier)
//self.client?.startSubscriptions(subscriptionInfo: subscriptionInfo)
subscriptionInfo.forEach { self.client?.startNewSubscription(subscriptionInfo: $0) }
}
Couldn't find any side effect with this approach yet, apart from requiring to fork the iOS SKD
I'm building an iOS App that is using Amazon MobileHub. Currently it's associated with Cognito and the sign up and sign in flow works great.
What I'm trying to achieve is for one user to be able to add another user as a friend so to speak. To make that possible, I need to check if a user with a specific username exists and if it does, get some attributes such as the name of that target user.
I've tried to use the get user > get details function but it gives me an error Authentication delegate not set.
Here's the code I used:
var pool: AWSCognitoIdentityUserPool?
let user = pool?.getUser(usernameField.text!)
self.pool = AWSCognitoIdentityUserPool.init(forKey: AWSCognitoUserPoolsSignInProviderKey)
user?.getDetails().continueWith { (task: AWSTask<AWSCognitoIdentityUserGetDetailsResponse>) -> Any? in
if let error = task.error as NSError? {
print("ERROR: " + error.localizedDescription)
return ""
}
print(task.result)
return ""
}
An approach I thought of was to store the username and the attributes I want to access to DynamoDB and then access it there but that will just create double unnecessary entries.
The issue you'll run into is that user attributes aren't publicly visible. Only the user who has signed in can call GetUser. If it's a back end process, you could do this via the AdminGetUser operation, but this looks client side so I wouldn't recommend that. The only real way around this would be to do what you suggested at the bottom of your post, ultimately.
i build a register form for my app and i need to send the user a verifiation code by sms in order to complete the registration proccess.
i tried to use MFMessageComposeViewController but its open the dialog sms on the device so the user can see the code.
i also checked the web for 3party of sending sms but there is a problem with the country code. i know its posible becuse whatsapp do it to confirm the user.
what it the right way to do it?
this is the topic the i tried:
Sending SMS in iOS with Swift
The best way to achieve this is by creating some views for allowing the user to enter the phone number with the country code which can be used by a server to send a request for initiating the OTP verification. To achieve this you need to:
Create View Controllers.
Upload Phone Number and Country code to the server.
Validate the requests by verifying the OTP.
As mentioned by Dan, you can use Digits in Fabric for that purpose, and create custom views for a great UX.
On the other hand, you can also use a service called as SendOTP from MSG91 - you can use it for internal testing and development ideas as they provide you with 5,000 free OTP SMS. The service has a complete set of APIs which you can implement on the backend as well on the app front. Also, they provide a framework so that you don't need to create the views, but only presentViewController and call delegate methods for knowing what happened during the verification process - such as Cancelled or Verified or Failed.
Swift implementation of the same looks like this:
class OTPFrame: UIViewController, sendOTPAuthenticationViewControllerDelegate {
func loadOTPFramework() {
SendOTP.sharedManager().startWithApiId("yourAppID")
let frameworkPath: NSString = NSBundle.mainBundle().privateFrameworksPath!
let frameworkBundlePath: NSString = frameworkPath.stringByAppendingPathComponent("SendOTPFramework.framework")
let frameworkBundle : NSBundle
= NSBundle(path: frameworkBundlePath as String)!
let authenticationViewController: AuthenticationViewController = AuthenticationViewController(nibName: "AuthenticationViewController", bundle: frameworkBundle)
authenticationViewController.delegate = self self.presentViewController(authenticationViewController, animated: true, completion: nil)
}
func authenticationisSuccessfulForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Success")
}
func canceledAuthentication() {
print("Cancelled")
}
func authenticationisFailedForMobileNumber(mobNo: String!, withCountryCode countryCode: String!) {
print("Failed")
}
}
Disclaimer: I, in no way, endorse the services mentioned above - you are free to choose whatever you like.
Thank You!
I would give digits a try! It's part of the Twitter Fabric package and it's very simple to use. The user enters their phone number and Fabric takes care of validating the number.
So I have been trying to integrate Stripe with my swift code and so far I have the following code for testing. I printed the credit card number because it is required to create a token on Stripe's end, and it correctly reflects the testing credit card number (e.g. 4242424242424242) used by Stripe (I also did input random numbers for cvc and exp date), however, the "validateCardReturningError" always skips the "createTokenWithCard" function to execute "print("Error")". In an attempt to debug, I also commented out "validateCardReturningError" to see if I get a token back, but instead I get nil as the value for token.
This behavior leads me to believe that the communication to Stripe isn't actually happening, therefore no token is being generated. If so, is there a way to test the communication? Alternatively, is there a way to check to see what value is being returned from "validateCardReturningError"?
if (userExpiration.isEmpty){ let expArr = userExpiration.componentsSeparatedByString("/")
if (expArr.count > 1) {
let expMonth: NSNumber = Int(expArr[0])!
let expYear: NSNumber = Int(expArr[1])!
creditCard.expMonth = expMonth.unsignedLongValue
creditCard.expYear = expYear.unsignedLongValue } }
print(creditCard.number)
do {
try creditCard.validateCardReturningError()
STPAPIClient.sharedClient().createTokenWithCard(
creditCard,
completion: { (token: STPToken?, stripeError: NSError?) -> Void in
print(token)
})
} catch {
print("Error")
}
The errors I get when I printed stripeError is:
Optional(Error Domain=com.stripe.lib Code=50 "Missing required param: exp_month." UserInfo={com.stripe.lib:ErrorMessageKey=Missing required param: exp_month., com.stripe.lib:ErrorParameterKey=card[expMonth], NSLocalizedDescription=Missing required param: exp_month.})
Change:
if (userExpiration.isEmpty)
to
if (!userExpiration.isEmpty)
so that you actually use the expiration date when there's something in there.
Also add an else clause to complain if it's empty, and prevent submission (I suppose this code is run when the user submits the form).
Stripe actually lets you browse all of the logs for your account in your dashboard. So if you head to https://dashboard.stripe.com/logs?method=not_get, you'll be able to see if you're successfully POSTing to the /v1/tokens endpoint.