How to add PayPal (live) to my Swift app? - paypal

This app is a two-part app - Uber-like app with a rider and a driver.
I am trying to make an app that requires PayPal. The person using the app (driver) will be accepting payments from others users of the app (rider). I have tried to follow the PayPal docs but just running into complications.
AppDelegate.swift:
PayPalMobile .initializeWithClientIds(forEnvironments:
[PayPalEnvironmentProduction: "CLIENT ID FOR PRODUCTION",
PayPalEnvironmentSandbox: "CLIENT ID FOR SANDBOX"])
ViewController.swift:
// create PayPal object:
var payPalConfig = PayPalConfiguration()
// Declare Payment Configuration
var environment: String = PayPalEnvironmentSandbox {
willSet(newEnvironment) {
if (newEnvironment != environment) {
PayPalMobile.preconnect(withEnvironment: newEnvironment)
}
}
}
var acceptCreditCards: Bool = true {
didSet {
payPalConfig.acceptCreditCards = acceptCreditCards
}
}
override func viewDidLoad() {
super.viewDidLoad()
payPalConfig.acceptCreditCards = acceptCreditCards
payPalConfig.merchantName = "DevHopes"
payPalConfig.merchantPrivacyPolicyURL = URL(string: "https://www.paypal.com/webapps/mpp/ua/privacy-full")
payPalConfig.merchantUserAgreementURL = URL(string: "https://www.paypal.com/webapps/mpp/ua/useragreement-full")
payPalConfig.languageOrLocale = Locale.preferredLanguages[0]
payPalConfig.payPalShippingAddressOption = .both
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
PayPalMobile.preconnect(withEnvironment: environment)
}
payPalPressed:
#IBAction func payPalPressed(_ sender: Any) {
let item1 = PayPalItem(name: "Staying Uptown", withQuantity: 1, withPrice: NSDecimalNumber(string: "0.01"), withCurrency: "CAD", withSku: "UPTOWN")
let item2 = PayPalItem(name: "North-South", withQuantity: 1, withPrice: NSDecimalNumber(string: "0.01"), withCurrency: "CAD", withSku: "North-South")
let item3 = PayPalItem(name: "South-SJRH", withQuantity: 1, withPrice: NSDecimalNumber(string: "0.01"), withCurrency: "CAD", withSku: "South-SJRH")
let items = [item1, item2, item3]
let subtotal = PayPalItem.totalPrice(forItems: items)
// Optional: include payment details
let paymentDetails = PayPalPaymentDetails(subtotal: subtotal, withShipping: nil, withTax: nil)
let total = subtotal
let payment = PayPalPayment(amount: total, currencyCode: "CAD", shortDescription: "Transportation", intent: .sale)
payment.items = items
payment.paymentDetails = paymentDetails
if (payment.processable) {
let paymentViewController = PayPalPaymentViewController(payment: payment, configuration: payPalConfig, delegate: self)
present(paymentViewController!, animated: true, completion: nil)
}
else {
// This particular payment will always be processable. If, for
// example, the amount was negative or the shortDescription was
// empty, this payment wouldn't be processable, and you'd want
// to handle that here.
print("Payment not processable: \(payment)")
}
}
// PayPalPaymentDelegate
func payPalPaymentDidCancel(_ paymentViewController: PayPalPaymentViewController) {
print("PayPal Payment Cancelled")
paymentViewController.dismiss(animated: true, completion: nil)
}
func payPalPaymentViewController(_ paymentViewController: PayPalPaymentViewController, didComplete completedPayment: PayPalPayment) {
print("PayPal Payment Success !")
paymentViewController.dismiss(animated: true, completion: { () -> Void in
// send completed confirmaion to your server
print("Here is your proof of payment:\n\n\(completedPayment.confirmation)\n\nSend this to your server for confirmation and fulfillment.")
})
}
When I run this, I get an error, even though the client id is correct:
When I put in my live credentials and change ..Sandbox to ..Production, I get an error: dictionary literal has duplicate keys?
I have the PayPal iOS SDK installed.

Related

How to set UISwitch state when receiving data from API

I have a screen with Two UISwitch buttons.
I am receiving a data in API:
"CustomerSubscribed": [
{
"Customer_ID": "100132",
"Mobile": "3213103428",
"Subscription": "Bill Payment",
"Mnemonic": "BILL",
"IsActive": 0
},
{
"Customer_ID": "100132",
"Mobile": "3213103428",
"Subscription": "Funds Transfer",
"Mnemonic": "IBFT",
"IsActive": 1
}
]
These are my two switches:
private var fundSwitch: UISwitch = {
let switchView = UISwitch()
switchView.isOn = false
switchView.onImage = UIImage(named: "switchOn")
switchView.offImage = UIImage(named: "switchOff")
switchView.onTintColor = UIColor.purple
switchView.tag = 1
switchView.addTarget(self, action: #selector(switchStateDidChange), for: .valueChanged)
switchView.translatesAutoresizingMaskIntoConstraints = false
return switchView
}()
private var billSwitch: UISwitch = {
let switchView = UISwitch()
switchView.isOn = false
switchView.onImage = UIImage(named: "switchOn")
switchView.offImage = UIImage(named: "switchOff")
switchView.onTintColor = UIColor.purple
switchView.tag = 2
switchView.addTarget(self, action: #selector(switchStateDidChange), for: .valueChanged)
switchView.translatesAutoresizingMaskIntoConstraints = false
return switchView
}()
On the basis of isActive, I have to set the values of UISWitch. If it is true, then switch must be on, otherwise if it is false then it must be Off.
This is what im trying right now:
#objc func switchStateDidChange(_ sender: UISwitch) {
if sender.tag == Switch.fundTrasnfer.rawValue {
print( sender.isOn ? customerSubscribe?[0].isActive ?? false: "Fund Transfer Switch is Off" )
} else {
print( sender.isOn ? customerSubscribe?[1].isActive ?? false: "Bill Payment Fund Transfer Switch is Off" )
}
}
and another what I tried:
#objc func switchStateDidChange(_ sender: UISwitch) {
for customer in self.customerSubscribe! {
if customer.isActive == true {
print(sender.isOn ? "Switch is ON": "Switch is Off")
} else {
print(sender.isOn ? "Switch is ON": "Switch is Off")
}
}
}
these are two approaches I have tried. I am using VIP Architecture, and I received response here:
extension SubscriptionViewController: SubscribeViewProtocol {
func saveSuccessCustomerData(response: CustomerSubscribeEntity.CustomerServiceResponse) {
}
func successSetupSubscription(response: SubscriptionEntity.SubscriptionServicesResponse) {
DispatchQueue.main.async {
print("Id \(response.customerSubscribed?[0].customerID)")
self.customerSubscribe = response.customerSubscribed
self.fundTransferLbl.text = self.customerSubscribe?[0].subscription
self.billPaymentLbl.text = self.customerSubscribe?[1].subscription
}
}
}
Then I created customerSubscribe to have the response in it:
var customerSubscribe: [SubscriptionEntity.CustomerSubscribed]?
My screen:
UI Is is programmatically, and i have both fund and bill switch defined.
Can I make a loop and check too instead of comparing taking index?
You are still hiding a lot of implementation detail that would be neccassary to give a definitive answer. So I have to make some assumptions.
isActive is of type boolean
successSetupSubscription lives in the same place as your switch vars
you create your switches programaticly and add them to your view.
Your function switchStateDidChange will react to changes done by the user. If you want to change the state of the switch programmaticly you need to set it to the switch itself.
func successSetupSubscription(response: SubscriptionEntity.SubscriptionServicesResponse) {
DispatchQueue.main.async {
print("Id \(response.customerSubscribed?[0].customerID)")
self.customerSubscribe = response.customerSubscribed
self.fundTransferLbl.text = self.customerSubscribe?[0].subscription
self.billPaymentLbl.text = self.customerSubscribe?[1].subscription
//add this
self.fundSwitch.isOn = response.customerSubscribed[0].isActive
self.billSwitch.isOn = response.customerSubscribed[1].isActive
}
}
You can try
switchOne.isOn = customerSubscribe?.first?.isActive == 1
switchTwo.isOn = customerSubscribe?.last?.isActive == 1

Eureka in swift List Selection Selected Value Automatically Deselect when navigating away

I am implementing Eureka list selection. On the language selections, there are 7 values to choose from. Value 1 to 6 is good. But when the last one selected and user navigates away from the VC, it's deselected automatically. I wonder if there is something wrong with my code. I have tried to "hardcode" the last selection on viewWillAppear just so the app goes smoothly and yes it is. But it's a little weird because none of the selection selected. I am a beginner to programming and greatly appreciated any help. Thank you
import Eureka
class ListSectionsController: FormViewController {
let defaults = UserDefaults.standard
var languageSelected = "English (default)"
override func viewDidAppear(_ animated: Bool) {
defaults.set(languageSelected, forKey: "LanguageSelection")
}
override func viewWillAppear(_ animated: Bool) {
if let language = defaults.string(forKey: "LanguageSelection") {
if language == "Korean 한국어" {
languageSelected = language
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let languageSelections = ["English (default)", "Spanish", "Japanese 日本語", "Chinese Mandarin 普通话", "Indonesian", "Dutch - het Nederlands", "Korean 한국어"]
form +++ SelectableSection<ImageCheckRow<String>>() { section in
section.header = HeaderFooterView(title: "Language Selection")
}
for option in languageSelections {
if let language = self.defaults.string(forKey: "LanguageSelection") {
self.form.setValues([language : language])
} else {
self.form.setValues([languageSelected : languageSelected])
}
form.last! <<< ImageCheckRow<String>(option){ lrow in
lrow.title = option
lrow.selectableValue = option
lrow.value = nil
}
}
}
override func valueHasBeenChanged(for row: BaseRow, oldValue: Any?, newValue: Any?) {
if row.section === form[0] {
if let selected = (row.section as! SelectableSection<ImageCheckRow<String>>).selectedRow()?.baseValue {
print("Selected: \(selected)")
languageSelected = selected as! String
defaults.set(languageSelected, forKey: "LanguageSelection")
}
} else if row.section === form[1] {
print("Mutiple Selection:\((row.section as! SelectableSection<ImageCheckRow<String>>).selectedRows().map({$0.baseValue}))")
}
}
}

In app purchase restore button doesn't cause any action?

I am new to working in in-app purchases. I have set up my app to allow multiple non-consumable in-apps. If it is a first time purchase it works perfectly. If I try and click the buy button again it shows "This in-app has already been purchased etc. etc." once you click Okay, it does nothing. I have noticed it only shows "Okay" as the option and not "Cancel" and "Okay". In my test app, it shows both and works great.` #IBOutlet weak var buyProductID: UILabel!
let product1 = "TestAd.com"
#IBOutlet weak var adView1: UIView!
func buyProduct1(product1: SKProduct){
print("Sending the Payment Request to Apple 1");
let payment1 = SKPayment(product: product1)
SKPaymentQueue.default().add(payment1);
}
#IBAction func product1Btn(sender: AnyObject) {
buyProductID.text = "Product1"
print("About to fetch the product... 1")
// Can make payments
if (SKPaymentQueue.canMakePayments())
{
let productID1:NSSet = NSSet(object: self.product1);
let productsRequest1:SKProductsRequest = SKProductsRequest(productIdentifiers: productID1 as! Set<String>);
productsRequest1.delegate = self;
productsRequest1.start();
print("Fetching Products 1");
}else{
print("Can't make purchases 1");
}
}
func purchase1ViewDid(){
if (UserDefaults.standard.bool(forKey: "purchased1")){
adView1.isHidden = true
print("No ads for 1")
} else {
print("Yes ads 1")
}
}
func productsRequest (_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
//PRODUCT 1
let count1 : Int = response.products.count
if (count1>0) {
let validProduct1: SKProduct = response.products[0] as SKProduct
if (validProduct1.productIdentifier == self.product1) {
print(validProduct1.localizedTitle)
print(validProduct1.localizedDescription)
print(validProduct1.price)
buyProduct1(product1: validProduct1);
} else {
print(validProduct1.productIdentifier)
}
} else {
print("nothing 1")
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Error Fetching product information 1");
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions1: [SKPaymentTransaction]) {
print("Received Payment Transaction Response from Apple 1");
for transaction1:AnyObject in transactions1 {
if let trans:SKPaymentTransaction = transaction1 as? SKPaymentTransaction{
switch trans.transactionState {
case .purchased:
if buyProductID.text == "Product1" {
print("Product Purchased 1");
SKPaymentQueue.default().finishTransaction(transaction1 as! SKPaymentTransaction)
// Handle the purchase
UserDefaults.standard.set(true , forKey: "purchased1")
adView1.isHidden = true
}
break;
case .failed:
print("Purchased Failed 1");
SKPaymentQueue.default().finishTransaction(transaction1 as! SKPaymentTransaction)
break;
case .restored:
print("Already Purchased 1");
SKPaymentQueue.default().restoreCompletedTransactions()
// Handle the purchase
UserDefaults.standard.set(true , forKey: "purchased1")
adView1.isHidden = true
break;
default:
break;
}
}
}
}
`
Non-consumable purchases can only be purchased once, so this is the expected behavior. The message is displayed because you can only purchase the non-consumable item once, and it remains associated with the account. It thinks that since you have purchased the item already, that you simply want to restore it. As a side note, for testing purposes, IAPs have to be tested using a real device, so the simulator won't test IAPs correctly.

How can my OS X app accept drag-and-drop of picture files from Desk in Cocoa?

I got an error when I drag files to my macOS app,
[sandbox] Failed to get a sandbox extension,when i set App Sandboxvalue boolean no,it is ok,but i want put my app to appstore,I must set App Sandbox YES, how can I do?
class FYOpenDragFileView: NSView{
override func draggingEnded(_ sender: NSDraggingInfo) {
print("松手了")
setupWithActive(active: false)
}
override func draggingExited(_ sender: NSDraggingInfo?) {
isDraging = false
setupWithActive(active: false)
print("draggingExited 进去又出来")
}
override func updateDraggingItemsForDrag(_ sender: NSDraggingInfo?) {
guard let next = delegate else {
return;
}
next.fileDraging()
print("更新拖动文件")
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let items = sender.draggingPasteboard.pasteboardItems else{
return false
}
var datas = [Data]()
for i in 0..<items.count{
let item = items[i] .string(forType: .fileURL)
if item != nil {
// this have an error
//[sandbox] Failed to get a sandbox extension
let da = try? Data(contentsOf: URL(string: item!)!)
guard let next = da else {
continue
}
datas.append(next)
}
}
QiniuUploadManger.uploadImage(nil, data: datas)
return true
}
}
With sandboxing, you'll have to set the appropriate "entitlements" for your app to receive drag-&-dropped files, which is probably why you're getting this error.
The entitlements file should already have been generated for you when you enabled Sandboxing. See this Apple documentation for details.

swift - Populate STPPaymentCardTextField Programmatically

I'm developing an app and using Stripe SDK and Card.io SDK. What i want to happen is populate the STPPaymentCardTextField Card Number,Expiry Month and Year with Card.io scanned credit card value. I Tried:
var paymentField = STPPaymentCardTextField()
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
var scanViewController: CardIOPaymentViewController = CardIOPaymentViewController(paymentDelegate: self)
paymentField.cardNumber = cardInfo.cardNumber
paymentField.expirationMonth = cardInfo.expiryMonth
paymentField.expirationYear = cardInfo.expiryYear
paymentViewController.dismissViewControllerAnimated(true, completion: nil)
}
I'm having an error Cannot assign to the result of this expression for each paymentField append.
What do you think i can do with this? Thanks!
STPPaymentCardTextField fields are read-only and you can only "get" from those properties.
STPPaymentCardTextField is used to collect credit card details. In your case you are already doing that using CardIOCreditCardInfo. Once you have the credit card details you can assemble the data into an STPCardParams object.
Once you've collected the card number, expiration, and CVC, package them up in an STPCardParams object and invoke the createTokenWithCard: method on the STPAPIClient class.
Now your method may look like this...
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
let card: STPCardParams = STPCardParams()
card.number = info.cardNumber
card.expMonth = info.expiryMonth
card.expYear = info.expiryYear
card.cvc = info.cvv
STPAPIClient.sharedClient().createTokenWithCard(card, completion: {(result, error) -> Void in
if error == nil {
// Either save the card token with your customer data or charge them right away
//createBackendChargeWithToken(token)
}
else {
//handleError(error)
}
})
}
paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
}
Update May be the right answer to your question.
import Stripe
class PaymentCardEditorField: STPPaymentCardTextField {
func setExistingCard(card: STPCardParams) {
replaceField("numberField", withValue: card.number!)
replaceField("expirationField", withValue: String(format: "%02d/%02d", card.expMonth, (card.expYear % 100)))
replaceField("cvcField", withValue: card.cvc!)
}
func replaceField(memberName: String, withValue value: String) {
let field = self.valueForKey(memberName) as! UITextField
let delegate = self as! UITextFieldDelegate
let len = field.text?.characters.count
if delegate.textField?(field, shouldChangeCharactersInRange: NSMakeRange(0, len!), replacementString: value) ?? false {
field.text = value
}
}
}
And then call the setExistingCard
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
let card: STPCardParams = STPCardParams()
card.number = info.cardNumber
card.expMonth = info.expiryMonth
card.expYear = info.expiryYear
card.cvc = info.cvv
paymentTextField.setExistingCard(card)
}
Works like a charm.
Follow this thread for potential update to Stripe SDK for in built support in the future.
https://github.com/stripe/stripe-ios/issues/127
I got a much cleaner answer from here
let cardParams = STPCardParams()
cardParams.number = "4242424242424242"
cardParams.expMonth = 07 // this data type is UInt and *not* Int
cardParams.expYear = 19 // this data type is UInt and *not* Int
cardParams.cvc = "123"
let paymentField = STPPaymentCardTextField()
paymentField.cardParams = cardParams //the paymentTextField will now show the cc #, exp, and cvc from above
You can get other test cards numbers like Mastercard, Amex, and Discover from here from Stripe