Customer does not have linked source with id - swift

I keep getting the following error when viewing stripe test trasactions:
{
"error": {
"type": "invalid_request_error",
"message": "Customer cus_9tW8Cf0Xvm9lRv does not have a linked source with ID tok_19Zj5rAANnhOmz4ex3ri2jtW.",
"param": "source",
"code": "missing"
}
}
my swift code to call the api is as follows:
#IBAction func payButtonWasPressed() {
stripeCard = STPCardParams()
let expirationDate = self.cardExpiryTextField.text!.components(separatedBy: "/")
let expMonth = UInt(expirationDate[0])
let expYear = UInt(expirationDate[1])
stripeCard.number = self.cardNumberTextField.text
stripeCard.cvc = self.cardCVVTextField.text
stripeCard.expMonth = expMonth!
stripeCard.expYear = expYear!
STPAPIClient.shared().createToken(withCard: stripeCard, completion: { (token, error) -> Void in
if error != nil {
self.handleError(error! as NSError)
return
}
self.chargeUsingToken(token!)
})
}
func handleError(_ error: NSError) {
UIAlertView(title: "Please Try Again",
message: error.localizedDescription,
delegate: nil,
cancelButtonTitle: "OK").show()
}
func chargeUsingToken(_ token: STPToken) {
let URL = "https://splitterstripeserver.herokuapp.com/charge"
let params = ["source": token.tokenId,
"stripe_token": token.tokenId,
"amount": total] as [String : Any]
let manager = AFHTTPSessionManager()
manager.post(URL, parameters: params, success: { (operation, responseObject) -> Void in
if let response = responseObject as? [String: String] {
UIAlertView(title: response["status"],
message: response["message"],
delegate: nil,
cancelButtonTitle: "OK").show()
}
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
and I am using the example backend found on the stripe github
On my app I get a pop-up saying Request failed: payment required (402). I have tried a lot of different things but can't seem to get successful response. I cant see what im doing wrong and need a fresh pair of eyes. Any help would be great. Thanks

Once you create the card token with Stripe Checkout or Stripe.js, you send it to your server where you use it to create a customer via the API. That card becomes "saved" with that customer and you can then charge it in the future. If you want to add another card to the same customer you would use the Create Card API to pass the new token and add that card.
When you create a charge, you pass the customer id (cus_XXX) in the customer parameter to charge the default card. If you want to charge a specific card, you would pass the card id (card_YYY) in the source parameter along with the customer.
At the moment, you are trying to pass a customer id and a card token which is not supported. You'll need to save that card on the customer first and then pass the new card id in the source parameter as mentioned above.

Related

How to create a ZkSync account?

The ZkSync documentation states that a deposit has to be made from an existing Ethereum account to a non-existing ZkSync account counterfactually in order to create a new account.
However, the method that is used to make the deposit already takes a ZkSync account address as a parameter to specify the recipient:
/// #notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
/// #param _zkSyncAddress The receiver Layer 2 address
function depositETH(address _zkSyncAddress) external payable {
require(_zkSyncAddress != SPECIAL_ACCOUNT_ADDRESS, "P");
require(msg.value > 0, "M"); // Zero-value deposits are forbidden by zkSync rollup logic
requireActive();
registerDeposit(0, SafeCast.toUint128(msg.value), _zkSyncAddress);
}
How do we pass a ZkSync account address if that's the very thing we're trying to achieve?
If you take a look at one of examples the ZkSync team provided for the Swift SDK:
let ethereumProvider = try self.wallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())
firstly {
ethereumProvider.deposit(token: .ETH,
amount: amount,
userAddress: wallet.address) /// Ethereum wallet address is provided
}.done { (_) in
self.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
}.catch { (error) in
self.present(UIAlertController.for(error: error), animated: true, completion: nil)
}
The parameter for the deposit is the address of the sender which is the account on the Ethereum network, not ZkSync.
Source
func depositETH(address: EthereumAddress, value: BigUInt) -> Promise<TransactionSendingResult> {
guard let tx = self.contract.write("depositETH",
parameters: [address] as [AnyObject], // here the parameter passed for the depositETH method of the smart contract is the address of the sender's account, which is the Ethereum account.
transactionOptions: createOptions(value: value)) else {
return Promise(error: ZkSyncContractError.invalidParameters)
}
return tx.sendPromise()
}
If you execute above code, you get the following error:
This mysterious error seems to indicate a connection error according to the web3swift source code, since it seems to say error 1.
public enum Web3Error: Error {
case transactionSerializationError
case connectionError
case dataError
case walletError
case inputError(desc:String)
case nodeError(desc:String)
case processingError(desc:String)
case keystoreError(err:AbstractKeystoreError)
case generalError(err:Error)
case unknownError
So what address do you have to pass to the deposit method in order to create a new ZkSync account? Is the Swift SDK example correct by passing a sender's address of an Ethereum account? If so, what is the source of the error?
Update
The sequence of the deposit process in the Swift SDK's example code goes something like following:
A wallet is created
guard let wallet = try? DefaultWallet<ChangePubKeyECDSA, DefaultEthSigner>(ethSigner: ethSigner,
zkSigner: zkSigner,
provider: provider) else {
fatalError()
}
The wallet is passed to a depositing view controller and is used to send a deposit.
let ethereumProvider = try self.wallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())
firstly {
ethereumProvider.deposit(token: .ETH,
amount: amount,
userAddress: wallet.address)
}.done { (_) in
self.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
}.catch { (error) in
self.present(UIAlertController.for(error: error), animated: true, completion: nil)
}
But, if my understanding if correct, the userAddress parameter in the deposit method is supposed to be the recipient's address. wallet.address, however, is the address of the sender.
public var address: String {
self.ethSigner.address
}
Prior to calling the deposit method, I generated a new Ethereum address to send the funds to in hopes that it could generate a new account in Zksync, but still got the same error.
KeysService().getWalletPrivateKey(password: password, completion: { [weak self] privateKey, error in
if let error = error {
print(error)
}
if let privateKey = privateKey {
do {
guard let newWallet = self?.createZKWallet(.rinkeby, privateKey: privateKey) else { return }
let ethereumProvider = try newWallet.createEthereumProvider(web3: Web3.InfuraRinkebyWeb3())
firstly {
ethereumProvider.deposit(token: .ETH,
amount: amount,
userAddress: newWallet.address)
}.done { (_) in
self?.present(UIAlertController.for(message: "Successfully deposited"), animated: true, completion: nil)
}.catch { (error) in
self?.present(UIAlertController.for(error: error), animated: true, completion: nil)
}
} catch {
self?.present(UIAlertController.for(error: error), animated: true, completion: nil)
}
}
})
You need to pass a new address (recipient address). Can't send from a 'non-existing' address as you state.
You can 'create' the address by generating it randomly from a private key, or by copying it if you know the address already.
Related to your problem, the example app uses an account that currently doesn't have any funds on Ethereum (0x46a23e25df9a0f6c18729dda9ad1af3b6a131160) (see screenshot). I've sent a small amount and it will work now.
to create a zkSync account we need to interact with our wallet somehow: f.e.: We can make a deposit from any decentralized wallet L1→L2 and it will mean that account was created or make a transfer to the existing address l2→l2 and it will be also workable. But to work with this funds we will need activate our L2 - make CPK, pay some fee. Otherwise we will need to do alternative withdraw.

How to send paypal information to firebase swift code?

I want to access to the information that I have to save for legal after user pay by using PayPal. Here is the code when user select to pay by PayPal, but how can I fetch the information that PayPal give after user complete the payment. And how can I send them to firebase?
#IBAction func btnPaypal(_ sender: UIButton) {
let payPalDriver = BTPayPalDriver(apiClient: braintreeClient)
payPalDriver.viewControllerPresentingDelegate = self
payPalDriver.appSwitchDelegate = self // Optional
// Specify the transaction amount here. "2.32" is used in this example.
let request = BTPayPalRequest(amount: lblSubtotal.text!)
request.currencyCode = "USD" // Optional; see BTPayPalRequest.h for more options
payPalDriver.requestOneTimePayment(request) { (tokenizedPayPalAccount, error) in
if let tokenizedPayPalAccount = tokenizedPayPalAccount {
print("Got a nonce: \(tokenizedPayPalAccount.nonce)")
// Access additional information
print("------------ paypal receipt -------------")
self.payPal = true
let email = tokenizedPayPalAccount.email
debugPrint(email!)
let firstName = tokenizedPayPalAccount.firstName
debugPrint(firstName!)
let lastName = tokenizedPayPalAccount.lastName
debugPrint(lastName!)
let phone = tokenizedPayPalAccount.phone
debugPrint(phone as Any)
// ....
let postalAddress = tokenizedPayPalAccount.billingAddress
// Access the properties of the address
debugPrint(postalAddress as Any)
let streetAddress = postalAddress?.streetAddress
debugPrint(streetAddress as Any)
let extendedAddress = postalAddress?.extendedAddress
debugPrint(extendedAddress as Any)
let locality = postalAddress?.locality
debugPrint(locality as Any)
let countryCodeAlpha2 = postalAddress?.countryCodeAlpha2
debugPrint(countryCodeAlpha2 as Any)
let postalCode = postalAddress?.postalCode
debugPrint(postalCode as Any)
let region = postalAddress?.region
debugPrint(region as Any)
// See BTPostalAddress.h for details
// let billingAddress = tokenizedPayPalAccount.billingAddress
// debugPrint(billingAddress as Any)
let shippingAddress = tokenizedPayPalAccount.shippingAddress
debugPrint(shippingAddress!)
for obj in self.arrayCart {
let invoiceID = self.ref.childByAutoId().key
let userID = Auth.auth().currentUser?.uid
let invoice = [
"invoiceID" : invoiceID,
"buyerID" : userID,
"payPal info" : ["payPal email" : email,
"first name" : firstName,
"last name" : lastName,
"phone" : phone,
"postalAddress" : postalAddress,
"streetAddress" : streetAddress,
"extendedAddress" : extendedAddress,
"locality" : locality,
"countryCodeAlpha2" : countryCodeAlpha2,
"postalCode" : postalCode,
"region" : region,
"shippingAddress" : shippingAddress]
] as [String : Any]
self.ref.child("invoice").child(invoiceID!).setValue(invoice)
}
print("------------ paypal receipt end -------------")
} else if let error = error {
// Handle error here...
print("error -------- \(error)")
} else {
// Buyer canceled payment approval
// alert
}
Error:
'InvalidFirebaseData', reason: '(setValue:) Cannot store object of type BTPostalAddress at payPal info.shippingAddress. Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.'
The error messsage is pretty clear; you're attempting to store an object in Firebase Realtime Database that's not an object Firebase accepts.
Here's the object you're attempting to store
let shippingAddress = tokenizedPayPalAccount.shippingAddress
and Firebase only supports NSNumber, NSString, NSDictionary, and NSArray. See Basic Write Operations for more info.
To fix it, you're going to need to deconstruct that object into primitive values, probably easiest to cast all of it's properties to strings and store that in a child node
payPal info
payPal email: "some email"
first name: "some first name"
shippingAddressWithChildData:
streetAddress: "some street address" //from the `tokenizedPayPalAccount.billingAddress` object
extended: "some additional street address info"
If you really need to store that particular object, Firebase does support NSData so you could cast the whole thing to an NSData object and store that but I would not suggest that path.

How do I get my AWS Cognito access token for updating user attributes?

I have the structure set up for updating user attributes, in this case the preferred username to use as an alias for signing in.
var attributes = [AWSCognitoIdentityUserAttributeType]()
let prefUsername = AWSCognitoIdentityUserAttributeType();
prefUsername?.name = "preferred_username";
prefUsername?.value = usernameField.text!;
attributes.append(prefUsername!);
let attributesRequest = AWSCognitoIdentityProviderUpdateUserAttributesRequest();
attributesRequest.userAttributes = attributes;
idProvider?.updateUserAttributes(attributesRequest)
Only thing I have no idea how to do is get the access token. I've looked in as much documentation as I could think of but I had no luck finding place to get access token.
You can use the api to initiate auth and get an AccessToken from the AuthenticationResult.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
/// Function to retreive the current token for the logged in user.
///
/// - Parameter completion: A completion block with an error or the token. Called back on the main thread.
public func getJWT(completion: #escaping((_ error: Error?, _ token: AWSCognitoIdentityUserSessionToken?) -> Void)) {
guard let user = self.pool.currentUser() else {
let nsError = NSError(domain: "JWT Error", code: 500, userInfo: ["message": "No Logged in user"])
completion(nsError, nil)
return
}
user.getSession().continueWith { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error {
completion(error, nil)
}else if let token = task.result?.idToken {
completion(nil, token)
}else {
completion(nil, nil)
}
}
}
}
Where self.pool is the AWSCognitoIdentityUserPool you hopefully set up correctly.
You would have to authenticate first to establish a session with Cognito User Pools. That session would contain an access token which you can then pass to every subsequent request. I see you are using the low level SDK methods. Here is a sample in swift for SignIn:
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift

Creating Dialog issue in private Chat with Quickblox

I am trying to implement private 1 to 1 chat with QuickBlox but following the Quickblox docs only shows for group chat in http://quickblox.com/developers/Chat#Create_dialog . When I try sending just single occupants_ids, it gives following error :
{
"errors": [
"Occupants_ids cannot be less than one."
]
}
I am hitting create Dialog API with following body :
{
"type": 3,
"name": "",
"occupant_id": "13822296"
}
Do I need to update some keys in my request body?
Please check: Create new 1-1(private) chat dialog
Code from documentaton work for me:
let chatDialog: QBChatDialog = QBChatDialog(dialogID: nil, type: QBChatDialogType.Private)
chatDialog.occupantIDs = [user.ID]
QBRequest.createDialog(chatDialog, successBlock: {(response: QBResponse?, createdDialog: QBChatDialog?) in completion?(response: response, createdDialog: chatDialog)
print("sucess + \(response)")
}, errorBlock: {(response: QBResponse!) in
print("response + \(response)")
})
QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:null type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(1530190)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
} errorBlock:^(QBResponse *response) {
}];
you can use this and you should provide one occupantIds. If it works please let me know.
let user = QBUUser()
user.id = UInt(arrDoctors[sender.tag].QuickBloxId)!
user.fullName = arrDoctors[sender.tag].title.capitalizeFirst
ServicesManager.instance().chatService.createPrivateChatDialog(withOpponent: user) { (response, dialog) in
let chatvc = CHAT_STORYBOARD.instantiateViewController(withIdentifier: "ChatViewController") as! ChatViewController
chatvc.dialog = dialog
self.navigationController?.pushViewController(chatvc, animated: true)
}

Verification code always invalid using Swift Parse and Twilio

Im using SMS verification to verify users. My problem is that when I enter a code to verify I get invalid code. I can't for the life of me figure out why.
Calling cloud code function:
#IBAction func verifyCodeButtonTapped(sender: AnyObject) {
var verificationCode: String = verificationCodeTextField.text!
let textFieldText = verificationCodeTextField.text ?? ""
if verificationCode.utf16.count != 4 {
displayAlert("Error", message: "You must entert the 4 digit verification code sent yo your phone")
} else {
let params = ["verifyPhoneNumber" : textFieldText]
PFCloud.callFunctionInBackground("verifyPhoneNumber", withParameters: params, block: { (object: AnyObject?, error) -> Void in
if error == nil {
self.performSegueWithIdentifier("showVerifyCodeView", sender: self)
} else {
self.displayAlert("Sorry", message: "We couldnt verify you. Please check that you enterd the correct 4 digit code sent to your phone")
}
})
}
}
Cloud code to verify code:
Parse.Cloud.define("verifyPhoneNumber", function(request, response) {
var user = Parse.User.current();
var verificationCode = user.get("phoneVerificationCode");
if (verificationCode == request.params.phoneVerificationCode) {
user.set("phoneNumber", request.params.phoneNumber);
user.save();
response.success("Success");
} else {
response.error("Invalid verification code.");
}
});
Twilio developer evangelist here.
In the Parse code, you are expecting request.params.phoneVerificationCode but when you call the cloud function from iOS you let params = ["verifyPhoneNumber" : textFieldText].
So, either change that line to
let params = ["phoneVerificationCode" : textFieldText]
so that it matches the cloud code. Or change your cloud code to
if (verificationCode == request.params.verifyPhoneNumber) {
so that it matches the iOS code.