Integrating Paypal SDK with native iOS App - ios

I am referring and integrating PayPal-iOS-SDK with my native iOS app.
I have successfully integrated the app(this is what I feel )
Please refer the code
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
PayPalMobile .initializeWithClientIdsForEnvironments([PayPalEnvironmentProduction: "YOUR_CLIENT_ID_FOR_PRODUCTION",
PayPalEnvironmentSandbox: "SANDBOX CODE"])
return true
}
View Controller
//
// ViewController.swift
// PaypalDemo
//
import UIKit
class ViewController: UIViewController,PayPalPaymentDelegate {
var environment:String = PayPalEnvironmentNoNetwork {
willSet(newEnvironment) {
if (newEnvironment != environment) {
PayPalMobile.preconnectWithEnvironment(newEnvironment)
}
}
}
#if HAS_CARDIO
// You should use the PayPal-iOS-SDK+card-Sample-App target to enable this setting.
// For your apps, you will need to link to the libCardIO and dependent libraries. Please read the README.md
// for more details.
var acceptCreditCards: Bool = true {
didSet {
payPalConfig.acceptCreditCards = acceptCreditCards
}
}
#else
var acceptCreditCards: Bool = false {
didSet {
payPalConfig.acceptCreditCards = acceptCreditCards
}
}
#endif
var resultText = "" // empty
var payPalConfig = PayPalConfiguration() // default
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Do any additional setup after loading the view.
title = "PayPal SDK Demo"
//successView.hidden = true
// Set up payPalConfig
print("this is credit card status \(acceptCreditCards)")
payPalConfig.acceptCreditCards = true;
payPalConfig.merchantName = "Awesome Shirts, Inc."
payPalConfig.merchantPrivacyPolicyURL = NSURL(string: "https://www.paypal.com/webapps/mpp/ua/privacy-full")
payPalConfig.merchantUserAgreementURL = NSURL(string: "https://www.paypal.com/webapps/mpp/ua/useragreement-full")
payPalConfig.languageOrLocale = NSLocale.preferredLanguages()[0]
// Setting the payPalShippingAddressOption property is optional.
//
// See PayPalConfiguration.h for details.
payPalConfig.payPalShippingAddressOption = .PayPal;
print("PayPal iOS SDK Version: \(PayPalMobile.libraryVersion())")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
PayPalMobile.preconnectWithEnvironment(environment)
}
#IBAction func payByPaypal(sender: UIButton) {
print("integrate paypal here")
resultText = ""
// Note: For purposes of illustration, this example shows a payment that includes
// both payment details (subtotal, shipping, tax) and multiple items.
// You would only specify these if appropriate to your situation.
// Otherwise, you can leave payment.items and/or payment.paymentDetails nil,
// and simply set payment.amount to your total charge.
// Optional: include multiple items
let item1 = PayPalItem(name: "Old jeans with holes", withQuantity: 2, withPrice: NSDecimalNumber(string: "84.99"), withCurrency: "USD", withSku: "Hip-0037")
let item2 = PayPalItem(name: "Free rainbow patch", withQuantity: 1, withPrice: NSDecimalNumber(string: "0.00"), withCurrency: "USD", withSku: "Hip-00066")
let item3 = PayPalItem(name: "Long-sleeve plaid shirt (mustache not included)", withQuantity: 1, withPrice: NSDecimalNumber(string: "37.99"), withCurrency: "USD", withSku: "Hip-00291")
let items = [item1, item2, item3]
let subtotal = PayPalItem.totalPriceForItems(items)
// Optional: include payment details
let shipping = NSDecimalNumber(string: "5.99")
let tax = NSDecimalNumber(string: "2.50")
let paymentDetails = PayPalPaymentDetails(subtotal: subtotal, withShipping: shipping, withTax: tax)
let total = subtotal.decimalNumberByAdding(shipping).decimalNumberByAdding(tax)
let payment = PayPalPayment(amount: total, currencyCode: "AUD", shortDescription: "My Shop", intent: .Sale)
payment.items = items
payment.paymentDetails = paymentDetails
if (payment.processable) {
let paymentViewController = PayPalPaymentViewController(payment: payment, configuration: payPalConfig, delegate: self)
presentViewController(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 processalbe: \(payment)")
}
}
func payPalPaymentDidCancel(paymentViewController: PayPalPaymentViewController) {
print("PayPal Payment Cancelled")
resultText = ""
// successView.hidden = true
paymentViewController.dismissViewControllerAnimated(true, completion: nil)
}
func payPalPaymentViewController(paymentViewController: PayPalPaymentViewController, didCompletePayment completedPayment: PayPalPayment) {
print("PayPal Payment Success !")
paymentViewController.dismissViewControllerAnimated(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.")
self.resultText = completedPayment.description
self.showSuccess(completedPayment.confirmation)
})
}
func showSuccess(inputString: NSObject) {
print("this is done successfully :) ")
print("send this to server \(inputString)")
}
}
My concerns regarding the app is
1.Whatever login I am using I am able to login . I have tried tim#cook.com as email and 1234 as password, still I was able to login.
2.I have tried using credit card as 4242-4242-4242-4242, cvv 123 12/12 and still payment was successful.
3.These are responses I am getting
[client: {
environment = mock;
"paypal_sdk_version" = "2.14.3";
platform = iOS;
"product_name" = "PayPal iOS SDK";
}, response_type: payment, response: {
"create_time" = "2016-08-01T09:16:40Z";
id = "PAY-NONETWORKPAYIDEXAMPLE123";
intent = sale;
state = approved;
}]
for another,
{
client = {
environment = mock;
"paypal_sdk_version" = "2.14.3";
platform = iOS;
"product_name" = "PayPal iOS SDK";
};
response = {
"create_time" = "2016-08-01T09:59:31Z";
id = "API-PAYMENT-ID-1843";
intent = sale;
state = approved;
};
"response_type" = payment;
}
Is the integration successful, or is there something that I am missing, because in response from paypal I don't find unique order id, not amount that has been charged from the user.
EDIT 1
This is info I am getting from
print("this is also info (completedPayment.description)")
from
func payPalPaymentViewController(paymentViewController: PayPalPaymentViewController, didCompletePayment completedPayment: PayPalPayment)
`CurrencyCode: AUD
Amount: 216.46
Short Description: My Shop
Intent: sale
Processable: Already processed
Display: $216.46
Confirmation: {
client = {
environment = mock;
"paypal_sdk_version" = "2.14.3";
platform = iOS;
"product_name" = "PayPal iOS SDK";
};
response = {
"create_time" = "2016-08-01T11:05:51Z";
id = "PAY-NONETWORKPAYIDEXAMPLE123";
intent = sale;
state = approved;
};
"response_type" = payment;
}
Details: Subtotal: 207.97, Shipping: 5.99, Tax: 2.5
Shipping Address: (null)
Invoice Number: (null)
Custom: (null)
Soft Descriptor: (null)
BN code: (null)
Item: '{2|Old jeans with holes|84.99|USD|Hip-0037}'
Item: '{1|Free rainbow patch|0.00|USD|Hip-00066}'
Item: '{1|Long-sleeve plaid shirt (mustache not included)|37.99|USD|Hip-00291}'`
Please correct, me where I am lacking.

If you go through below code
var environment:String = PayPalEnvironmentNoNetwork {
willSet(newEnvironment) {
if (newEnvironment != environment) {
PayPalMobile.preconnectWithEnvironment(newEnvironment)
}
}
}
Which is no network environment it will give you all the time success.
You need to change environment as per your requirement. For e.g. If you have sandbox user registered on PayPal then you need to change like this
var environment:String = PayPalEnvironmentSandbox
{
willSet(newEnvironment) {
if (newEnvironment != environment)
{
PayPalMobile.preconnectWithEnvironment(newEnvironment)
}
}
}
And then if you try with any other user rather than registered sandbox user it will give you error. This will work for only sandbox user.
There is one more environment called Production which is for live purpose that you need to ask credentials from your client.
PayPalEnvironmentProduction
I hope the above information is clear which you was looking for.
Don't forget to change client IDs in AppDelegate as per your respective environment
PayPalMobile.initializeWithClientIdsForEnvironments([PayPalEnvironmentProduction:
"", PayPalEnvironmentSandbox:
"AYP......"])

Related

Cannot create an Envelope using online signing mode

I'm trying to create an Envelop and sign it using online mode.
At first, I logged in to my account
#IBAction private func signDocument(_ sender: UIButton) {
guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
isLoading = true
DSMManager.login(withEmail: Environment.current.docuSignUserID,
password: Environment.current.docuSignPass,
integratorKey: Environment.current.docuSignIntegratorKey,
host: hostURL) { [weak self] info, error in
self?.isLoading = false
if let error = error {
self?.error = error
} else {
self?.showDocuSign(info: info)
}
}
}
// MARK: - Helpers
private func showDocuSign(info: DSMAccountInfo?) {
guard let info = info else { return }
envelopManager.perform(with: info, presentingController: self)
}
After that, I created my test Envelop:
final class EnvelopeManager {
private let envelopesManager = DSMEnvelopesManager()
private let templateManager = DSMTemplatesManager()
// MARK: - Lifecycle
func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
guard let documentURL = R.file.tgkCapitalPortfolioBAgreementPdf(),
let documentData = try? Data(contentsOf: documentURL) else { return }
let envelopDefinition = DSMEnvelopeDefinition()
envelopDefinition.envelopeName = "Some name"
envelopDefinition.emailSubject = "Please Sign Envelope on Document"
envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
let document = DSMDocument()
document.name = R.file.tgkCapitalPortfolioBAgreementPdf.name
document.documentId = "1"
document.documentBase64 = documentData.base64EncodedString()
envelopDefinition.documents = [document]
let signHere = DSMSignHere()
signHere.documentId = document.documentId
signHere.pageNumber = 1
signHere.recipientId = "1"
signHere.anchorXOffset = 100
signHere.anchorYOffset = 100
signHere.tabId = "1"
let tabs = DSMTabs()
tabs.signHereTabs = [signHere]
let signer = DSMSigner()
signer.canSignOffline = false
signer.email = config.email
signer.name = config.userName
signer.recipientId = "1"
signer.tabs = tabs
let signers: [DSMSigner] = [signer]
let recipients = DSMRecipients()
recipients.signers = signers
envelopDefinition.recipients = recipients
envelopDefinition.status = "sent"
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .online) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
}
private func presentSigning(presenter: UIViewController, envelopeID: String) {
envelopesManager.presentSigning(withPresenting: presenter,
envelopeId: envelopeID,
animated: true) { (viewController, error) in
if let viewController = viewController {
print(viewController)
}
if let error = error {
print(error.localizedDescription)
}
}
}
}
But here
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .online) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
I have an error:
Envelope creation online is not supported at this moment. Please try
offline mode
When I switched to offline mode I can't use the method
envelopesManager.presentSigning(withPresenting:, enveloped: , animated:, completeion)
Because it works only for envelops which were created in the online mode. And in my case completion block is not executed.
How to resolve this issue? How I can create an envelope in online mode and sign it? What I'm doing wrong? I just want to select my pdf which contains in the project bundle and in some position add the sign.
The sample project which was provided here doesn't match my requirements. Because there is using a template from server and creating envelop via presentComposeEnvelopeController method for choosing document and etc.
XCode 12.4, iOS 13/14, DocuSign 2.4.1 included in the project via CocoaPods.
Edit1
I have updated my perform method:
func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
guard let documentURL = R.file.tgkCapitalPortfolioBAgreementPdf(),
let documentData = try? Data(contentsOf: documentURL) else { return }
let envelopDefinition = DSMEnvelopeDefinition()
envelopDefinition.envelopeName = "Some name"
envelopDefinition.emailSubject = "Please Sign Envelope on Document"
envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
let document = DSMDocument()
document.name = R.file.tgkCapitalPortfolioBAgreementPdf.name
document.documentId = "1"
document.documentBase64 = documentData.base64EncodedString()
envelopDefinition.documents = [document]
let signHere = DSMSignHere()
signHere.documentId = document.documentId
signHere.pageNumber = 1
signHere.recipientId = "1"
signHere.frame = .init(originX: 100,
originY: 100,
width: 100,
height: 100,
originYOffsetApplied: 50)
signHere.tabId = "1"
let tabs = DSMTabs()
tabs.signHereTabs = [signHere]
let signer = DSMSigner()
signer.email = config.email
signer.name = config.userName
signer.recipientId = "1"
signer.tabs = tabs
let signers: [DSMSigner] = [signer]
let recipients = DSMRecipients()
recipients.signers = signers
envelopDefinition.recipients = recipients
envelopDefinition.status = "created"
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
}
and presentSigning method too:
private func presentSigning(presenter: UIViewController, envelopeID: String) {
envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
envelopeId: envelopeID) { (viewController, error) in
if let viewController = viewController {
print(viewController)
}
if let error = error {
print(error.localizedDescription)
}
}
}
But now I have got the error in the presentSigning method: Envelope is ready for sync and can not be resumed for signing.
And any screen with my pdf document was not shown. How to resolve it? How I can preview this document and after that add the ability for a user to sign it?
Solution
The working code of the EnvelopManager class:
final class EnvelopeManager {
private let envelopesManager = DSMEnvelopesManager()
private let templateManager = DSMTemplatesManager()
// MARK: - Lifecycle
func sync() {
envelopesManager.syncEnvelopes()
}
func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
guard let path = Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name, ofType: "pdf") else { return }
let envelopDefinition = DSMEnvelopeDefinition()
envelopDefinition.envelopeName = "Some name"
envelopDefinition.emailSubject = "Please Sign Envelope on Document"
envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
let builder = DSMDocumentBuilder()
builder.addDocumentId("1")
builder.addName(R.file.tgkCapitalPortfolioBAgreementPdf.name)
builder.addFilePath(Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name,
ofType: "pdf")!)
let document = builder.build()
envelopDefinition.documents = [document]
let signHere = DSMSignHere()
signHere.documentId = document.documentId
signHere.pageNumber = 1
signHere.recipientId = "1"
signHere.frame = .init(originX: 100,
originY: 100,
width: 100,
height: 100,
originYOffsetApplied: 50)
signHere.tabId = "1"
let tabs = DSMTabs()
tabs.signHereTabs = [signHere]
let signer = DSMSigner()
signer.email = config.email
signer.name = config.userName
signer.userId = config.userId
signer.clientUserId = config.userId
signer.routingOrder = 1
signer.recipientId = "1"
signer.tabs = tabs
let signers: [DSMSigner] = [signer]
let recipients = DSMRecipients()
recipients.signers = signers
envelopDefinition.recipients = recipients
envelopDefinition.status = "created"
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
}
private func presentSigning(presenter: UIViewController, envelopeID: String) {
envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
envelopeId: envelopeID) { (viewController, error) in
if let viewController = viewController {
print(viewController)
}
if let error = error {
print(error.localizedDescription)
}
}
}
}
After you will have signed the document do not forget to call the sync method on the top view controller
But now I have got the error in the presentSigning method: Envelope is ready for sync and can not be resumed for signing.
Envelope is ready for sync and can not be resumed for signing. denotes the state that envelope has no local signers awaiting signing. This could happen because of a few reasons.
Taking a look at the adding recipient section of guide, this adds a Remote Signer to local envelope.
// Create an envelope recipient with name and email and assign an id and type with routing order
DSMEnvelopeRecipient *recipient = [[[[[[DSMRecipientBuilder builderForType: DSMRecipientTypeSigner]
addRecipientId: #"1"]
addSignerName: #"Jane Wood"]
addSignerEmail: #"JaneWood#docusign.com"]
addRoutingOrder: 1]
build];
No local signers have been added to capture signatures. If you need to add a local signer they could represent following cases:
SDK authenticated account could be used as a signer (DSMRecipientTypeSigner). In the example: #"JaneWood#docusign.com" is logged in with SDK with routingOrder (or SigningOrder) of 1.
SDK authenticated account could be used as a host to an in-person-signer (DSMRecipientTypeInPersonSigner). The following example would add an in-person-signer for #"JaneWood#docusign.com" as host:
// Create an envelope recipient with name and email and assign an id and type with routing order
DSMEnvelopeRecipient *recipient = [[[[[[[DSMRecipientBuilder builderForType: DSMRecipientTypeInPersonSigner]
addRecipientId: #"1"]
addHostName: #"Jane Wood"]
addHostEmail: #"JaneWood#docusign.com"]
addSignerName: #"IPS - John Local Doe"]
addRoutingOrder: 1]
build];
Only remote signers (non-local SDK account) have been added to envelope: Invoking syncEnvelopes on such envelopes will send documents and email the remote-signers to complete signing.
Note: All of the local signers have already completed signing: You'd reach this case once all local signatures have been captured, and using syncEnvelopes would send envelope to DocuSign servers.
#igdev: Thanks for adding code snippets and details explaining your approach.
How I can create an envelope in online mode and sign it?
As of current SDK version, creating online envelopes with EnvelopeDefinition is not yet supported.
Following method is only supported for remote-envelopes that have been already created (via DocuSign API or Web) and accessible on the DocuSign account under use by Native-SDK.
envelopesManager.presentSigning(withPresenting:, enveloped: , animated:, completion)
I just want to select my pdf which contains in the project bundle and in some position add the sign.
Compose flow based envelope creation would enable you to use the PDF from the bundle, position tabs and present signing in the offline mode. Here is a guide (Compose Envelope) that lists steps with sample code to get the desired result. Following snippet from compose envelope guide shows how such envelope can be presented for signing:
if (envelope) {
// Compose the envelope to automatically cache it
[self.envelopesManager composeEnvelopeWithEnvelopeDefinition: envelope
signingMode: DSMSigningModeOffline
completion: ^(NSString * _Nullable envelopeId, NSError * _Nonnull error) {
// error checks in case envelope compose failed. Also use notifications for caching related events.
if (error) { ... }
// Resume the envelope to start the signing process
[self.envelopesManager resumeSigningEnvelopeWithPresentingController: self
envelopeId: envelopeId
completion: ^(UIViewController * _Nullable presentedController, NSError * _Nullable error) {
// error checks in case UI presentation failed. Use notifications for other events.
if (error) { ... }
}];
}
];
}
The sample code prior to update (thanks for adding it) to use the tab-frame instead of anchors is correct step as anchors aren't supported in offline mode by the SDK. In order to avoid such issues, it's best to use the EnvelopeBuilder (which in turn uses Recipient and TabBuilder) to create tabs for individual recipients and assign correct frames (e.g. signHere.frame = CGRect(x: 100, y: 100, width: 50, height: 40)). Using EnvelopeBuilder also ensures that custom envelope data goes through validation process.
/*!
* #discussion add frame of the tab (e.g. CGFrameMake(100, 100, 50, 40) for Sign Tab) and return builder.
* #param frame a frame to draw tab within
*/
- (DSMTabBuilder *)addFrame:(CGRect)frame;
Check answer below for envelope ready for sync state:

Requiring consent before proceeding with survey in ResearchKit

I'm currently creating a program that wants to utilize ResearchKit. Before getting into survey questions, I will need to require consent. I am using the Ray Wenderlich (https://www.raywenderlich.com/1820-researchkit-tutorial-with-swift-getting-started) tutorial and have this code set up. I've already tried to go on with the survey part of my program, and I can access it without even going through consent.
import ResearchKit
public var ConsentTask: ORKOrderedTask {
var ctr = 0
let Document = ORKConsentDocument()
Document.title = "Consent"
//section types
let sectionTypes: [ORKConsentSectionType] = [
.overview,
.dataGathering,
.privacy
]
let consentSections: [ORKConsentSection] = sectionTypes.map { contentSectionType in
let consentSection = ORKConsentSection(type: contentSectionType)
if ctr < sectionTypes.count {
if ctr == 0 {
consentSection.summary = "Summary"
consentSection.content = "Content"
}
else if ctr == 1 { //Data Gathering
consentSection.summary = "Summary"
consentSection.content = "Content"
}
else if ctr == 2 { //Privacy
consentSection.summary = "Summary"
consentSection.content = "Content"
}
ctr = ctr + 1
}
return consentSection
}
Document.sections = consentSections
Document.addSignature(ORKConsentSignature(forPersonWithTitle: nil, dateFormatString: nil, identifier: "UserSignature"))
var steps = [ORKStep]()
let visualConsentStep = ORKVisualConsentStep(identifier: "VisualConsent", document: Document)
steps += [visualConsentStep]
let signature = Document.signatures!.first! as ORKConsentSignature
let reviewConsentStep = ORKConsentReviewStep(identifier: "Review", signature: signature, in: Document)
reviewConsentStep.text = "Review the consent"
reviewConsentStep.reasonForConsent = "I agree"
steps += [reviewConsentStep]
//Completion
let completionStep = ORKCompletionStep(identifier: "CompletionStep")
completionStep.title = "Welcome"
completionStep.text = "Thank you for helping!"
steps += [completionStep]
return ORKOrderedTask(identifier: "ConsentTask", steps: steps)
}
And in my view controller, I have
#IBAction func consentTask(_ sender: Any) {
if consentDone == false {
let taskViewController = ORKTaskViewController(task: ConsentTask, taskRun: nil)
taskViewController.delegate = self
present(taskViewController, animated: true, completion: nil)
}
else if consentDone == true {
//would they like to leave the study
}
}
With my efforts of putting a consentDone flag here.
func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskViewControllerFinishReason, error: Error?) {
taskViewController.dismiss(animated: true, completion: {() in self.consentDone = true})
print(consentDone)
}
However, what happens is that if the user presses cancel on the top right or done at the very end of the consent, it will trigger this always. Is there a way to be able to make sure that a block of code is executed only after everything is finished? Ideally, after this, I would like to make this a flag that the user has already finished the consent. I will redirect the user to a different page every time afterwards until the user leaves the study.
After some trial and error, I found the answer here: https://github.com/ResearchKit/ResearchKit/issues/919
By knowing that the signature of the user means that the user has finished the form, we can do
if result.identifier == "UserSignature"{
print(result)
let consentDoneAnswerResult = result as! ORKConsentSignatureResult
let consentDone = consentDoneAnswerResult.consented
print(consentDone)
}
And this gives consentDone as true when the form is done, not canceled.

Add Shipping charge in Paypal checkout using Braintree SDK

I am implement the Braintree SDK for payment in my E-Commerce application(iOS and Android).but I am not able to and shipping charge in “BTPayPalRequest”.
How to add Shipping charge in this SDK?
My Code is below:
braintreeClient = BTAPIClient(authorization: “Key”)!
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: strAmmount)
/// Cart Item
var items = [BTPayPalLineItem]()
for Dict in ArrMyCartDetails{
let item = BTPayPalLineItem(quantity: String(Q), unitAmount: String(SP), name: strProductName, kind: .debit)
items.append(item)
}
request.lineItems = items
request.intent = .sale
request.displayName = AppTitle
request.isShippingAddressRequired = false
request.isShippingAddressEditable = false
request.shippingAddressOverride = BTPostalAddress.init()
request.currencyCode = "AUD"
payPalDriver.requestOneTimePayment(request) { (tokenizedPayPalAccount, error) in
if let tokenizedPayPalAccount = tokenizedPayPalAccount {
print("Got a nonce: \(tokenizedPayPalAccount.nonce)")
} else if error != nil {
// Handle error here...
self.ShowMessage(strMsg: "something wrong in payment process.")
} else {
// Buyer canceled payment approval
self.ShowMessage(strMsg: "you cancel payment!")
}
}

Paypal Payment issue of credit card payment

I have integrate the paypal payment but when i entered the credit card detail on live mode it returns the error-
UNAUTHORIZED_PAYMENT - The merchant does not accept payments of this type. (401)
I am using the business account and permissions are enabled.
var payPalConfig = PayPalConfiguration()
var environment:String = PayPalEnvironmentProduction {
willSet(newEnvironment) {
if (newEnvironment != environment) {
PayPalMobile.preconnect(withEnvironment: newEnvironment)
}
}
}
var acceptCreditCards: Bool = true {
didSet {
payPalConfig.acceptCreditCards = acceptCreditCards
}
}
func paypalPaymentGatewaySetup(){
payPalConfig.acceptCreditCards = acceptCreditCards;
payPalConfig.merchantName = "User"
payPalConfig.merchantPrivacyPolicyURL = NSURL(string: "https://www.paypal.com/webapps/mpp/ua/privacy-full") as URL?
payPalConfig.merchantUserAgreementURL = NSURL(string: "https://www.paypal.com/webapps/mpp/ua/useragreement-full") as URL?
payPalConfig.languageOrLocale = NSLocale.preferredLanguages[0]
payPalConfig.payPalShippingAddressOption = .payPal;
PayPalMobile.preconnect(withEnvironment: environment)
}
override func viewDidLoad() {
super.viewDidLoad()
paypalPaymentGatewaySetup()
}
Now i am calling function makingRequestForPayment on click of make payment button and i have import payment status delegate as well. But in paypal delegate method i am getting "The merchant does not accept this type of payment Unauthorized 401". I did app delegate setup with client ID of Production as well
func makingRequestForPayment(){
// let globalPriceInStr = UserDefaults.standard.value(forKey: "PriceForPayment") as? String ?? ""
let globalPriceInStr = "1"
let totalPrice = NSDecimalNumber(string: globalPriceInStr)
let item1 = PayPalItem(name: "Membership", withQuantity: 1, withPrice: totalPrice, withCurrency: "USD", withSku: "SivaGanesh-0001")
print(item1)
let items = [item1]
print(items)
let subtotal = PayPalItem.totalPrice(forItems: items)
let shipping = NSDecimalNumber(string: "0.00")
let tax = NSDecimalNumber(string: "0.00")
let paymentDetails = PayPalPaymentDetails(subtotal: subtotal, withShipping: shipping, withTax: tax)
let total = subtotal.adding(shipping).adding(tax)
let payment = PayPalPayment(amount: total, currencyCode: "USD", shortDescription: "Advertisement Submission", 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{
print("Payment not processable: \(payment)")
}
}

How can access contact identifier from callKit

I start callkit call with this code:
private func startCallKitCall(call: Call, completion: #escaping (Bool) -> ()){
let handle = CXHandle(type: .phoneNumber, value: call.handle)
let startCallAction = CXStartCallAction(call: call.uuid, handle: handle)
startCallAction.isVideo = call.hasVideo
startCallAction.contactIdentifier = call.uuid.uuidString
let transaction = CXTransaction(action: startCallAction)
requestCallKitTransaction(transaction, completionHandler: { success in
completion(success)
})
}
in contactIdentifier I set the user UUID, then in provider, update the caller remote name:
public func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
let update = CXCallUpdate()
update.remoteHandle = action.handle
update.hasVideo = action.isVideo
update.localizedCallerName = "TestCallName"
self.provider!.reportCall(with: action.callUUID, updated: update)
action.fulfill()
}
I use "TestCallName" as localizedCallerName, now on recent call of my phone, I see a row named "TestCallName", If I click it, I call this method on appdelegate:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if (userActivity.activityType == "INStartAudioCallIntent"){
guard let interaction = userActivity.interaction else {
return false
}
var personHandle: INPersonHandle?
if let startVideoCallIntent = interaction.intent as? INStartVideoCallIntent {
personHandle = startVideoCallIntent.contacts?[0].personHandle
} else if let startAudioCallIntent = interaction.intent as? INStartAudioCallIntent {
personHandle = startAudioCallIntent.contacts?[0].personHandle
print(startAudioCallIntent.contacts?[0])
}
print(personHandle!.value)
} else if (userActivity.activityType == "INStartVideoCallIntent"){
} else {
print("called userActivity: \(userActivity.activityType), but not yet supported.")
}
return true
}
but the output I obtain is:
Optional(<INPerson: 0x1742a5280> {
contactIdentifier = "<null>";
customIdentifier = "<null>";
displayName = "<null>";
image = "<null>";
nameComponents = "<null>";
personHandle = "<INPersonHandle: 0x17402f0c0> {\n label = \"<null>\";\n type = PhoneNumber;\n value = \"TestCallName\";\n}";
relationship = "<null>";
siriMatches = "<null>";
})
Optional("TestCallName")
i see only the name "TestCallName", but contact identifier is "null", how can I fix this? I see there is also a field name "customIdentifier", can I use this?
thanks!
The contactIdentifier is the ID of the contact in contacts app. The one you set into startCallAction.contactIdentifier means nothing. To call a user you need to use either phone or email or at least name.

Resources