Coder Social home page Coder Social logo

titanium-apple-pay's Introduction

Apple Pay in Appcelerator Titanium

Build Status License Contact

Titanium Apple Pay

Summary

This module provides access to the native iOS Apply Pay PassKit Framework. Using this module, you can easily accept Apple Pay payments using Titanium Mobile. The currently supported payment gateways are Stripe and Braintree, which are recommended by Apple to process payments easily.

Features

The following features are covered:

  • Payment Button
  • Payment Request
  • Payment Dialog
  • Payment backend-gateway using Stripe
  • All latest API's, up to iOS 12

Requirements

  • Titanium Mobile SDK 7.1.1+
  • Xcode 9 or later
  • iOS 9 or later

Setup

Unpack the module and place it inside the /modules/iphone folder of your project. Edit the modules section of your tiapp.xml file to include this module:

<modules>
    <module platform="iphone">ti.applepay</module>
</modules>

After that, you create an instance of the module by requiring it:

var ApplePay = require('ti.applepay');

Before you can start using the many different API's, you need to configure the module by providing a Stripe / Braintree API-Key (from here). The gateways are determined using constants to keep the module open for more payment gateways added in feature releases. The configuration looks like this:

ApplePay.setupPaymentGateway({
  name: ApplePay.PAYMENT_GATEWAY_STRIPE, // or: ApplePay.PAYMENT_GATEWAY_BRAINTREE
  apiKey: '<YOUR_STRIPE_OR_BRAINTREE_API_KEY>'
});

You also need the Entitlements.plist with the merchant group's you want to assign. A full tutorial on how to setup the merchant ID's in the developer center can be found here.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.in-app-payments</key>
	<array>
		<string>merchant.de.hansknoechel.paydemo.stripe</string>
		<string>merchant.de.hansknoechel.paydemo.braintree</string>
		<string>merchant.de.hansknoechel.paydemo.chase</string>
	</array>
</dict>
</plist>

(Optionally), you can create an 'Apple Pay styled' button by providing the type and style:

var payButton = ApplePay.createPaymentButton({
    type: ApplePay.PAYMENT_BUTTON_TYPE_BUY,
    style: ApplePay.PAYMENT_BUTTON_STYLE_WHITE_OUTLINE
});
payButton.addEventListener('click', openPaymentDialog);

Make sure to check if ApplePay is set-up by using the ApplePay.canMakePayments(args) method or display the ApplePay.openPaymentSetup() to let the user setup new cards. Now you're able to setup the payment request using ApplePay.createPaymentRequest(args):

var paymentRequest = ApplePay.createPaymentRequest({
    merchantIdentifier: 'merchant.com.company.appname',
    merchantCapabilities: ApplePay.MERCHANT_CAPABILITY_3DS | ApplePay.MERCHANT_CAPABILITY_CREDIT | ApplePay.MERCHANT_CAPABILITY_DEBIT | ApplePay.MERCHANT_CAPABILITY_EMV,
    countryCode: 'US',
    currencyCode: 'USD',
    billingContact: contact,
    shippingContact: contact,
    supportedNetworks: [ApplePay.PAYMENT_NETWORK_VISA, ApplePay.PAYMENT_NETWORK_MASTERCARD],
    requiredShippingAddressFields: ApplePay.ADDRESS_FIELD_POSTAL_ADDRESS,
    requiredBillingAddressFields: ApplePay.ADDRESS_FIELD_POSTAL_ADDRESS,
    shippingType: ApplePay.SHIPPING_TYPE_DELIVERY,
    shippingMethods: shippingMethods,
    summaryItems: summaryItems,
    applicationData: {
        userId: 1337
    }
});

Although most properties are self-describing, you can find all properties in the official Apple documentation as well, in addition to the detailed code documentation following in the next chapter.

To actually show the payment dialog, you can use the ApplePay.createPaymentDialog method:

var paymentDialog = ApplePay.createPaymentDialog({
    paymentRequest: paymentRequest
});
paymentDialog.show();

There are a couple of payment events you need to implement to respond to changes made in the payment dialog and to finish a payment:

paymentDialog.addEventListener('didSelectPayment', didSelectPayment);
paymentDialog.addEventListener('didSelectShippingContact', didSelectShippingContact);
paymentDialog.addEventListener('didSelectShippingMethod', didSelectShippingMethod);
paymentDialog.addEventListener('willAuthorizePayment', didAuthorizePayment);
paymentDialog.addEventListener('didAuthorizePayment', willAuthorizePayment);
paymentDialog.addEventListener('close', willClose);

function didSelectPayment(e) {
    e.handler.complete(paymentRequest.summaryItems);
}

function didSelectShippingContact(e) {
    e.handler.complete(ApplePay.PAYMENT_AUTHORIZATION_STATUS_SUCCESS, paymentRequest.shippingMethods, paymentRequest.summaryItems);
}

function didSelectShippingMethod(e) {
    e.handler.complete(ApplePay.PAYMENT_AUTHORIZATION_STATUS_SUCCESS, paymentRequest.summaryItems);
}

function willAuthorizePayment() {
  // Do amazing stuff here, before the payment is authorized.
}

function didAuthorizePayment(e) {
    // ... Send the encrypted payment data to your backend
    Ti.API.info('Payment successfully authorized: ' + e.success);
    
    e.handler.complete(ApplePay.PAYMENT_AUTHORIZATION_STATUS_SUCCESS);
}

The handler needs to call complete to tell the payment dialog to continue processing. For example, you can change the shipping costs depending on the selected shipping contact or decline a payment if your backend could not be reached.

This makes the module as flexible as it needs to be to handle even advanced payments. An example of using the module in a real-world-application can be found in example/app.js.

API Documentation

PaymentRequest

A payment request is initialized using the `ApplePay.createPaymentRequest method.

Properties

  • (String) merchantIdentifier: Your merchant identifier, e.g. merchant.com.company.app.
  • (Number) merchantCapabilities: A bit field of the payment processing constants (MERCHANT_CAPABILITY_*) you support.
  • (String) countryCode: The two-letter ISO 3166 country code, e.g. US.
  • (String) currencyCode: The three-letter ISO 4217 currency code, e.g. USD.
  • (Object) billingContact: An object representing a billing contact. Allowed properties:
    • (String) firstName
    • (String) middleName
    • (String) lastName
    • (String) prefix
    • (String) suffix
    • (Object) address
    • (String) street
    • (String) city
    • (String) zip
    • (String) state
    • (String) country
    • (String) ISOCountryCode
    • (String) subLocality (iOS 10.3+)
    • (String) subAdministrativeArea (iOS 10.3+)
    • (String) email
    • (String) phone
  • (Object) shippingContact: An object representing a shipping contact. Allowed properties are same as in billingContact.
  • (Array) supportedNetworks: The payment networks that you support, e.g. [ApplePay.PAYMENT_NETWORK_VISA, ApplePay.PAYMENT_NETWORK_MASTERCARD].
  • (Number) requiredShippingAddressFields: A bit field of shipping address field constants (ADDRESS_FIELD_*) that you need in order to process the transaction.
  • (Number) requiredBillingAddressFields: A bit field of billing address field constants (ADDRESS_FIELD_*) that you need in order to process the transaction.
  • (Number) shippingType: The type of shipping used by this request, one of SHIPPING_TYPE_*`.
  • (ShippingMethod) shippingMethods: An array of ApplePay.ShippingMethod objects that describe the supported shipping methods.
  • ([SummaryItem]) summaryItems: summaryItems: An array of ApplePay.SummaryItem objects that summarize the amount of the payment.
  • (Object) applicationData: Application-specific data or state, e.g. {"userId": 1337}

Methods

  • ([PAYMENT_NETWORK_*]) availableNetworks: Returns the available payment networks. The result is an array of PAYMENT_NETWORK_* constants, e.g. PAYMENT_NETWORK_MASTERCARD. This method is only available on iOS 10 and later.

PaymentDialog

A payment dialog is initialized using the ApplePay.createPaymentDialog method.

Properties

  • (PaymentRequest) paymentRequest. The payment request storing the payment-relevant data.

Methods

  • (void) open: Opens the payment dialog modally.

Events

  • didSelectPayment: Tells the application that the payment method has changed and asks for a list of updated summary items.
  • didSelectShippingContact: Tells the application that the user selected a shipping address:
    • (ShippingContactCompletionHandler) handler: The completion handler of the shipping contact. Call this in order to proceed with updated shipping options based on the new address.
    • (Object) contact: The updated shipping contact. Note that you won’t receive the full shipping address in this step due to privacy restrictions from Apple. You will only receive parts of the address to able to calculate the shipping. The complete shipping- and billing-contact infos are available in the didAuthorizePayment event.
  • didSelectShippingMethod: Tells the application that the user selected a shipping method:
    • (ShippingMethodCompletionHandler) handler: The completion handler of the shipping method. Call this in order to proceed with updated items based on the new shipping method.
    • (String) identifier: The identifier of the new shipping method.
  • willAuthorizePayment: Tells the application that the user is authorizing the payment request.
  • didAuthorizePayment: Tells the application that the user has authorized the payment request:
    • (AuthorizationCompletionHandler) handler: The completion handler of the authorized payment. Call this in order to finish the transaction after you processed the payment data (e.g. sending it to the server.
    • (Boolean) success: Indicates whether or not the authorization succeeded.
    • (Object) payment: An Object of payment-related data (paymentNetwork, paymentInstrumentName, paymentMethod, transactionIdentifier, shippingContact, billingContact and paymentData).
    • (Date) created: The exact timestamp of the time the payment was created (Available when using Stripe).
    • (String) stripeTokenId: The Stripe payment token ID of the processed payment (Available when using Stripe).
    • (String) braintreeNonce: The Braintree nonce token of the processed payment (Available when using Braintree).
  • close: Tells the application that payment authorization has completed and the dialog is closed.

PaymentButton

A payment button is initialized using the ApplePay.createPaymentButton method.

Properties

  • (Number) type: The button's content, one of PAYMENT_BUTTON_TYPE_*. Creation-only.
  • (Number) style: The button's appearance, one of PAYMENT_BUTTON_STYLE_*.Creation-only.
  • (Number) borderRadius: The button's border radius. iOS 12+

Events

  • click: Tells the application that the payment button was clicked.

ShippingMethod

A shipping method is initialized using the ApplePay.createShippingMethod method.

Properties

  • (String) identifier: A unique identifier for the shipping method, used by the app, e.g. "free_shipping".
  • (String) title: A short, localized description of the shipping method, e.g. "Free Shipping".
  • (String) description: A user-readable description of the shipping method, e.g. "3-5 working days".
  • (Number) price: The shipping method's price, e.g. 99.99.

SummaryItem

A summary item is initialized using the ApplePay.createSummaryItem method.

Properties

  • (Number) itemType: This property defaults to a PAYMENT_SUMMARY_ITEM_TYPE_FINAL type. For a list of possible summary item types, see PAYMENT_SUMMARY_ITEM_TYPE_*.
  • (String) title: A short, localized description of the summary item.
  • (Number) price: The summary item's price. Important: The total price of the shopping cart is not calculated automatically by the native API. You need to take care of the current amount of the shopping cart. The last summary item of your shopping cart normally is the name of your store and the total amount added together.

Constants

  • PAYMENT_BUTTON_TYPE_PLAIN

  • PAYMENT_BUTTON_TYPE_BUY

  • PAYMENT_BUTTON_TYPE_SETUP

  • PAYMENT_BUTTON_TYPE_IN_STORE (iOS 10.0+)

  • PAYMENT_BUTTON_TYPE_DONATE (iOS 10.2+)

  • PAYMENT_BUTTON_TYPE_CHECKOUT (iOS 12.0+)

  • PAYMENT_BUTTON_TYPE_SUBSCRIBE (iOS 12.0+)

  • PAYMENT_BUTTON_TYPE_BOOK (iOS 12.0+)

  • PAYMENT_BUTTON_STYLE_BLACK

  • PAYMENT_BUTTON_STYLE_WHITE

  • PAYMENT_BUTTON_STYLE_WHITE_OUTLINE

  • PAYMENT_METHOD_TYPE_CREDIT

  • PAYMENT_METHOD_TYPE_DEBUT

  • PAYMENT_METHOD_TYPE_PREPAID

  • PAYMENT_METHOD_TYPE_STORE

  • PAYMENT_SUMMARY_ITEM_TYPE_PENDING

  • PAYMENT_SUMMARY_ITEM_TYPE_FINAL

  • PAYMENT_AUTHORIZATION_STATUS_SUCCESS

  • PAYMENT_AUTHORIZATION_STATUS_FAILURE

  • PAYMENT_AUTHORIZATION_STATUS_INVALID_BILLING_POSTAL_ADDRESS

  • PAYMENT_AUTHORIZATION_STATUS_INVALID_SHIPPING_POSTAL_ADDRESS

  • PAYMENT_AUTHORIZATION_STATUS_INVALID_SHIPPING_CONTACT

  • PAYMENT_AUTHORIZATION_STATUS_PIN_REQUIRED (iOS 9.2+)

  • PAYMENT_AUTHORIZATION_STATUS_PIN_INCORRECT (iOS 9.2+)

  • PAYMENT_AUTHORIZATION_STATUS_PIN_LOCKOUT (iOS 9.2+)

  • PAYMENT_GATEWAY_NONE

  • PAYMENT_GATEWAY_STRIPE

  • PAYMENT_GATEWAY_BRAINTREE

  • PAYMENT_NETWORK_AMEX

  • PAYMENT_NETWORK_DISCOVER

  • PAYMENT_NETWORK_MASTERCARD

  • PAYMENT_NETWORK_VISA

  • PAYMENT_NETWORK_PRIVATE_LABEL

  • PAYMENT_NETWORK_CHINA_UNION_PAY (iOS 9.2+)

  • PAYMENT_NETWORK_INTERAC (iOS 9.2+)

  • PAYMENT_NETWORK_JCB (iOS 10.1+)

  • PAYMENT_NETWORK_SUICA (iOS 10.1+)

  • PAYMENT_NETWORK_ID_CREDIT (iOS 10.3+)

  • PAYMENT_NETWORK_QUIC_PAY (iOS 10.3+)

  • PAYMENT_NETWORK_CARTE_BANCAIRE (iOS 10.3+)

  • SHIPPING_TYPE_SHIPPING

  • SHIPPING_TYPE_DELIVERY

  • SHIPPING_TYPE_SERVICE_PICKUP

  • SHIPPING_TYPE_STORE_PICKUP

  • ADDRESS_FIELD_NONE

  • ADDRESS_FIELD_POSTAL_ADDRESS

  • ADDRESS_FIELD_PHONE

  • ADDRESS_FIELD_EMAIL

  • ADDRESS_FIELD_NAME

  • ADDRESS_FIELD_ALL

  • MERCHANT_CAPABILITY_3DS

  • MERCHANT_CAPABILITY_CREDIT

  • MERCHANT_CAPABILITY_DEBIT

  • MERCHANT_CAPABILITY_EMV

Author

Hans Knöchel (@hansemannnn / Web)

Support

Contact me on Ti.Slack

License

Apache 2.0

Contributing

Code contributions are greatly appreciated, please submit a new Pull Request!

titanium-apple-pay's People

Contributors

hansemannn avatar jasonkneen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

titanium-apple-pay's Issues

Apple Pay dialog stucks at "Processing"

Hello.
I got strange behaviour - Apple Pay dialog sometimes stucks at "Processing".
Logs shows that didAuthorizePayment event was not triggered:

[DEBUG] Ti.ApplePay: No payment provider selected, using own gateway.
[DEBUG] Ti.ApplePay: didSelectPayment

Checked on both - simulator and real device.
Merchant ID was properly setted.
Also checked with module's example/app.js.

Did you faced same issues?

Here is my example code.

function doClick(e) {
	const ApplePay = require('ti.applepay');
	ApplePay.setupPaymentGateway({});
	//Summary items
	let data = {
		value: 100,
		value_price: 100,
	};
	const company = 'ООО "Ливайн Торг"';
	const items = [{
		title: `a96`,
		price: 100
	}];
	const summaryItems = items.map(item => ApplePay.createSummaryItem({
		itemType: ApplePay.PAYMENT_SUMMARY_ITEM_TYPE_FINAL,
		title: item.title,
		price: item.price
	}));
	const totalPrice = items.reduce((sum, el) => sum + el.price, 0);

	// The native Apple Pay API receives the total as a summary item that
	// usually holds the company name and total price of the order.
	const summary = ApplePay.createSummaryItem({
		itemType: ApplePay.PAYMENT_SUMMARY_ITEM_TYPE_FINAL,
		title: company,
		price: totalPrice
	});

	summaryItems.push(summary);

	const paymentRequest = ApplePay.createPaymentRequest({
		merchantIdentifier: 'merchant.ua.com.avias.app',
		merchantCapabilities: ApplePay.MERCHANT_CAPABILITY_3DS,
		countryCode: 'UA',
		currencyCode: 'UAH',
		supportedNetworks: [ApplePay.PAYMENT_NETWORK_VISA, ApplePay.PAYMENT_NETWORK_MASTERCARD],
		summaryItems: summaryItems,
		applicationData: {
			userId: 24
		}
	});

	let paymentResult;
	const paymentDialog = ApplePay.createPaymentDialog({ paymentRequest });
	paymentDialog.addEventListener('didSelectPayment', didSelectPayment);
	paymentDialog.addEventListener('willAuthorizePayment', willAuthorizePayment);
	paymentDialog.addEventListener('didAuthorizePayment', didAuthorizePayment);
	

	paymentDialog.addEventListener('close', willClose);
	paymentDialog.show();

	function didSelectPayment(e) {
		Ti.API.debug("didSelectPayment");
		e.handler.complete(paymentRequest.summaryItems);
	}

	function willAuthorizePayment(e) {
		Ti.API.debug("willAuthorizePayment");
	}

	function didAuthorizePayment(e) {
		Ti.API.debug("didAuthorizePayment");
		// paymentResult = e.payment;
		e.handler.complete(ApplePay.PAYMENT_AUTHORIZATION_STATUS_SUCCESS);
		//send to server
	}

	function willClose(e) {
		Ti.API.debug("willClose");
		// handle cancelled payment
		if (!paymentResult) {
			// cb({cancelled: true});
		}
	}
}

Your payment information is formatted improperly.

When submitting an order to Stripe based on a currency, total and one summary item, when submitting I get the following:

Your payment information is formatted improperly. Please make sure you're correctly using the latest version of our iOS library. For more information see https://stripe.com/docs/mobile/ios. (Code: 50)

There's nothing else logged (i.e. what is being sent to the Stripe server) and this is on simulator using a simulated card.

ApplePay.canMakePayments() crashes the app

If you use ApplePay.canMakePayments() as described it'll bomb the app with:

[ERROR] message = "Invalid type passed to function";
[ERROR] nativeReason = "expected: Object, was: (null)";

A quick fix is to call ApplePay.canMakePayments({}) and then it'll work.

App Freezes

Thank you for this module, using as per the example code.

Opening the payment sheet is working fine, closing the payment sheet is fine, but if you click the Apple Pay button again the app freezes (not crash).

As I attempt to console log the app, it appears that when the Apple Pay button is clicked for the second time, I get "[DEBUG] Ti.ApplePay: Using Stripe integration!" for a second time before it freezes.

Currently using Titanium SDK 12.0.0.GA and iOS16.3.1 on device.

Any way to debug what the issue could be?

Thanks again

Content Typo

PAYMENT_AUTHORIZATION_STATUS_FAILTURE should be PAYMENT_AUTHORIZATION_STATUS_FAILURE?

Error while using the example

Hi @hansemannn,

Thank you for creating this module!

I created an account in Stripe and I am trying to use your example exactly like it is, but just adding a Stripe test key. I am getting the following error:

[ERROR] :  The application has crashed with an uncaught exception 'NSInvalidArgumentException'.
[ERROR] :  Reason:
[ERROR] :  *** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]
[ERROR] :  Stack trace:
[ERROR] :  0   CoreFoundation                      0x0000000114fa61ce __exceptionPreprocess + 270
[ERROR] :  1   libobjc.A.dylib                     0x0000000113f3a031 objc_exception_throw + 48
[ERROR] :  2   CoreFoundation                      0x0000000114fe60bc _CFThrowFormattedException + 194
[ERROR] :  3   CoreFoundation                      0x0000000114eb9951 -[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 321
[ERROR] :  4   CoreFoundation                      0x0000000114eb97db +[NSDictionary dictionaryWithObjects:forKeys:count:] + 59
[ERROR] :  5   Est. de Minas                       0x000000010b89de1a -[TiApplepayPaymentButton didTouchUpInside:] + 224
[ERROR] :  6   UIKit                               0x000000010df913e8 -[UIApplication sendAction:to:from:forEvent:] + 83
[ERROR] :  7   UIKit                               0x000000010e10c7a4 -[UIControl sendAction:to:forEvent:] + 67
[ERROR] :  8   UIKit                               0x000000010e10cac1 -[UIControl _sendActionsForEvents:withEvent:] + 450
[ERROR] :  9   UIKit                               0x000000010e10ba09 -[UIControl touchesEnded:withEvent:] + 580
[ERROR] :  10  UIKit                               0x000000010e0060bf -[UIWindow _sendTouchesForEvent:] + 2729
[ERROR] :  11  UIKit                               0x000000010e0077c1 -[UIWindow sendEvent:] + 4086
[ERROR] :  12  UIKit                               0x000000010dfab310 -[UIApplication sendEvent:] + 352
[ERROR] :  13  UIKit                               0x000000010e8ec6af __dispatchPreprocessedEventFromEventQueue + 2796
[ERROR] :  14  UIKit                               0x000000010e8ef2c4 __handleEventQueueInternal + 5949
[ERROR] :  15  CoreFoundation                      0x0000000114f48bb1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
[ERROR] :  16  CoreFoundation                      0x0000000114f2d4af __CFRunLoopDoSources0 + 271
[ERROR] :  17  CoreFoundation                      0x0000000114f2ca6f __CFRunLoopRun + 1263
[ERROR] :  18  CoreFoundation                      0x0000000114f2c30b CFRunLoopRunSpecific + 635
[ERROR] :  19  GraphicsServices                    0x0000000118d54a73 GSEventRunModal + 62
[ERROR] :  20  UIKit                               0x000000010df90057 UIApplicationMain + 159
[ERROR] :  21  Est. de Minas                       0x000000010b371694 main + 100
[ERROR] :  22  libdyld.dylib                       0x0000000117c2b955 start + 1

Could you help me, please?
My best!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.