@power-elements/stripe-elements
Advanced tools
Comparing version 2.1.1 to 2.2.0
@@ -6,3 +6,3 @@ { | ||
"name": "stripe-elements", | ||
"description": "[Stripe.js v3 Card Elements](https://stripe.com/docs/elements), but it's a Web Component!\nSupports Shadow DOM.\n\n👨🎨 [Live Demo](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--enter-a-stripe-publishable-key) 👀\n\n### 🧙♂️ Usage\nIf you prebuilt with Snowpack, load the module from your `web_modules` directory\n\n```html\n<script type=\"module\" src=\"/web_modules/@power-elements/stripe-elements/stripe-elements.js\"></script>\n```\n\nAlternatively, load the module from the unpkg CDN\n```html\n<script type=\"module\" src=\"https://unpkg.com/@power-elements/stripe-elements/stripe-elements.js?module\"></script>\n```\n\nThen you can add the element to your page.\n\n```html\n<script type=\"module\" src=\"https://unpkg.com/@power-elements/stripe-elements/stripe-elements.js?module\"></script>\n<stripe-elements id=\"stripe\"\n action=\"/payment\"\n publishable-key=\"pk_test_XXXXXXXXXXXXXXXXXXXXXXXX\"\n></stripe-elements>\n```\n\nSee the demos for more comprehensive examples.\n - Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-plain-html-and-javascript).\n - Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-lit-element).\n - Using `<stripe-elements>` in a [Polymer Element](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-polymer-element).\n - Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-vue-component).\n - Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-an-angular-component).\n - Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-react-component).\n - Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-preact-component).\n\n## Styling\n\nStripe v3's 'Stripe Elements' are not custom elements, but rather forms\nhosted by stripe and injected into your page via an iFrame. When we refer to the\n'Stripe Element' in this document, we are referring to the hosted Stripe form,\nnot the `<stripe-element>` custom element. But when I mention the 'element', I mean the custom element.\n\nWhen you apply CSS to the custom properties available, they're parsed and sent to Stripe, who should apply them to the Stripe Element they return in the iFrame.\n\n- `base` styles are inherited by all other variants.\n- `complete` styles are applied when the Stripe Element has valid input.\n- `empty` styles are applied when the Stripe Element has no user input.\n- `invalid` styles are applied when the Stripe Element has invalid input.\n\nThere are 11 properties for each state that you can set which will be read into the Stripe Element iFrame:\n\n- `color`\n- `font-family`\n- `font-size`\n- `font-smoothing`\n- `font-variant`\n- `icon-color`\n- `line-height`\n- `letter-spacing`\n- `text-decoration`\n- `text-shadow`\n- `text-transform`", | ||
"description": "[Stripe.js v3 Card Elements](https://stripe.com/docs/elements), but it's a Web Component!\nSupports Shadow DOM.\n\n👨🎨 [Live Demo](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--enter-a-stripe-publishable-key) 👀\n\n### 🧙♂️ Usage\nIf you prebuilt with Snowpack, load the module from your `web_modules` directory\n\n```html\n<script type=\"module\" src=\"/web_modules/@power-elements/stripe-elements/stripe-elements.js\"></script>\n```\n\nAlternatively, load the module from the unpkg CDN\n```html\n<script type=\"module\" src=\"https://unpkg.com/@power-elements/stripe-elements/stripe-elements.js?module\"></script>\n```\n\nThen you can add the element to your page.\n\n```html\n<stripe-elements id=\"stripe\"\n action=\"/payment\"\n publishable-key=\"pk_test_XXXXXXXXXXXXXXXXXXXXXXXX\"\n></stripe-elements>\n```\n\nSee the demos for more comprehensive examples.\n - Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-html--stripe-elements).\n - Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-litelement--stripe-elements).\n - Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vue--stripe-elements).\n - Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-angular--stripe-elements).\n - Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-react--stripe-elements).\n - Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-preact--stripe-elements).\n\n## Styling\n\nStripe v3's 'Stripe Elements' are not custom elements, but rather forms\nhosted by stripe and injected into your page via an iFrame. When we refer to the\n'Stripe Element' in this document, we are referring to the hosted Stripe form,\nnot the `<stripe-element>` custom element. But when I mention the 'element', I mean the custom element.\n\nWhen you apply CSS to the custom properties available, they're parsed and sent to Stripe, who should apply them to the Stripe Element they return in the iFrame.\n\n- `base` styles are inherited by all other variants.\n- `complete` styles are applied when the Stripe Element has valid input.\n- `empty` styles are applied when the Stripe Element has no user input.\n- `invalid` styles are applied when the Stripe Element has invalid input.\n\nThere are 11 properties for each state that you can set which will be read into the Stripe Element iFrame:\n\n- `color`\n- `font-family`\n- `font-size`\n- `font-smoothing`\n- `font-variant`\n- `icon-color`\n- `line-height`\n- `letter-spacing`\n- `text-decoration`\n- `text-shadow`\n- `text-transform`", | ||
"attributes": [ | ||
@@ -12,3 +12,3 @@ { | ||
"description": "Whether to hide icons in the Stripe form.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "false" | ||
@@ -19,3 +19,3 @@ }, | ||
"description": "Whether or not to hide the postal code field.\nUseful when you gather shipping info elsewhere.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "false" | ||
@@ -32,3 +32,3 @@ }, | ||
"description": "Prefilled values for form. Example {postalCode: '90210'}", | ||
"type": "Object", | ||
"type": "object", | ||
"default": "{}" | ||
@@ -39,31 +39,46 @@ }, | ||
"description": "The card brand detected by Stripe", | ||
"type": "String" | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "card", | ||
"description": "The Stripe card object.", | ||
"type": "stripe.Element" | ||
}, | ||
{ | ||
"name": "is-complete", | ||
"description": "If the form is complete.", | ||
"type": "Boolean", | ||
"name": "complete", | ||
"description": "Whether the form is complete.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "is-empty", | ||
"name": "empty", | ||
"description": "If the form is empty.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "true" | ||
}, | ||
{ | ||
"name": "stripe-ready", | ||
"description": "If the stripe element is ready to receive focus.", | ||
"type": "Boolean", | ||
"name": "invalid", | ||
"description": "Whether the form is invalid.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "card", | ||
"description": "The Stripe card object.\n**DEPRECATED**. Will be removed in a future version. use `element` instead", | ||
"type": "stripe.elements.Element", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "is-empty", | ||
"description": "Whether the form is empty.\n**DEPRECATED**. Will be removed in a future version. use `empty` instead", | ||
"type": "boolean", | ||
"default": "true", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "is-complete", | ||
"description": "Whether the form is complete.\n**DEPRECATED**. Will be removed in a future version. use `complete` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "payment-method", | ||
"description": "Stripe PaymentMethod", | ||
"type": "stripe.PaymentMethod" | ||
"type": "stripe.paymentMethod.PaymentMethod" | ||
}, | ||
@@ -86,2 +101,7 @@ { | ||
{ | ||
"name": "client-secret", | ||
"description": "The `client_secret` part of a Stripe `PaymentIntent`", | ||
"type": "String" | ||
}, | ||
{ | ||
"name": "generate", | ||
@@ -95,3 +115,3 @@ "description": "Type of payment representation to generate.", | ||
"description": "Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX`", | ||
"type": "String" | ||
"type": "string" | ||
}, | ||
@@ -120,11 +140,31 @@ { | ||
{ | ||
"name": "has-error", | ||
"description": "Whether the element has an error", | ||
"type": "Boolean", | ||
"name": "focused", | ||
"description": "If the element is focused.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "ready", | ||
"description": "Whether the stripe element is ready to receive focus.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "stripe", | ||
"description": "Stripe instance", | ||
"type": "stripe.Stripe" | ||
}, | ||
{ | ||
"name": "has-error", | ||
"description": "Whether the element has an error\n**DEPRECATED**. Will be removed in a future version. Use `error` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripe-ready", | ||
"description": "Whether the stripe element is ready to receive focus.\n**DEPRECATED**. Will be removed in a future version. use `ready` instead.", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
} | ||
@@ -137,3 +177,3 @@ ], | ||
"description": "Whether to hide icons in the Stripe form.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "false" | ||
@@ -145,3 +185,3 @@ }, | ||
"description": "Whether or not to hide the postal code field.\nUseful when you gather shipping info elsewhere.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "false" | ||
@@ -160,3 +200,3 @@ }, | ||
"description": "Prefilled values for form. Example {postalCode: '90210'}", | ||
"type": "Object", | ||
"type": "object", | ||
"default": "{}" | ||
@@ -168,37 +208,49 @@ }, | ||
"description": "The card brand detected by Stripe", | ||
"type": "String" | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "card", | ||
"attribute": "card", | ||
"description": "The Stripe card object.", | ||
"type": "stripe.Element" | ||
}, | ||
{ | ||
"name": "isComplete", | ||
"attribute": "is-complete", | ||
"description": "If the form is complete.", | ||
"type": "Boolean", | ||
"name": "complete", | ||
"attribute": "complete", | ||
"description": "Whether the form is complete.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "isEmpty", | ||
"attribute": "is-empty", | ||
"name": "empty", | ||
"attribute": "empty", | ||
"description": "If the form is empty.", | ||
"type": "Boolean", | ||
"type": "boolean", | ||
"default": "true" | ||
}, | ||
{ | ||
"name": "stripeReady", | ||
"attribute": "stripe-ready", | ||
"description": "If the stripe element is ready to receive focus.", | ||
"type": "Boolean", | ||
"name": "invalid", | ||
"attribute": "invalid", | ||
"description": "Whether the form is invalid.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "stripeMount", | ||
"description": "Stripe Element mount point", | ||
"type": "Element" | ||
"name": "card", | ||
"attribute": "card", | ||
"description": "The Stripe card object.\n**DEPRECATED**. Will be removed in a future version. use `element` instead", | ||
"type": "stripe.elements.Element", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "isEmpty", | ||
"attribute": "is-empty", | ||
"description": "Whether the form is empty.\n**DEPRECATED**. Will be removed in a future version. use `empty` instead", | ||
"type": "boolean", | ||
"default": "true", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "isComplete", | ||
"attribute": "is-complete", | ||
"description": "Whether the form is complete.\n**DEPRECATED**. Will be removed in a future version. use `complete` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "billingDetails", | ||
@@ -231,3 +283,3 @@ "description": "billing_details object sent to create the payment representation. (optional)", | ||
"description": "Stripe PaymentMethod", | ||
"type": "stripe.PaymentMethod" | ||
"type": "stripe.paymentMethod.PaymentMethod" | ||
}, | ||
@@ -253,2 +305,8 @@ { | ||
{ | ||
"name": "clientSecret", | ||
"attribute": "client-secret", | ||
"description": "The `client_secret` part of a Stripe `PaymentIntent`", | ||
"type": "String" | ||
}, | ||
{ | ||
"name": "generate", | ||
@@ -264,3 +322,3 @@ "attribute": "generate", | ||
"description": "Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX`", | ||
"type": "String" | ||
"type": "string" | ||
}, | ||
@@ -293,9 +351,16 @@ { | ||
{ | ||
"name": "hasError", | ||
"attribute": "has-error", | ||
"description": "Whether the element has an error", | ||
"type": "Boolean", | ||
"name": "focused", | ||
"attribute": "focused", | ||
"description": "If the element is focused.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "ready", | ||
"attribute": "ready", | ||
"description": "Whether the stripe element is ready to receive focus.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "stripe", | ||
@@ -305,2 +370,23 @@ "attribute": "stripe", | ||
"type": "stripe.Stripe" | ||
}, | ||
{ | ||
"name": "hasError", | ||
"attribute": "has-error", | ||
"description": "Whether the element has an error\n**DEPRECATED**. Will be removed in a future version. Use `error` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripeReady", | ||
"attribute": "stripe-ready", | ||
"description": "Whether the stripe element is ready to receive focus.\n**DEPRECATED**. Will be removed in a future version. use `ready` instead.", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripeMount", | ||
"description": "Stripe.js mount point element. Due to limitations in the Stripe.js library, this element must be connected to the document.", | ||
"type": "Element" | ||
} | ||
@@ -310,80 +396,68 @@ ], | ||
{ | ||
"name": "stripe-change", | ||
"name": "change", | ||
"description": "Stripe Element change event" | ||
}, | ||
{ | ||
"name": "stripe-ready", | ||
"description": "Stripe has been initialized and mounted" | ||
"name": "error", | ||
"description": "The validation error, or the error returned from stripe.com" | ||
}, | ||
{ | ||
"name": "brand-changed", | ||
"description": "The new value of brand" | ||
"name": "payment-method", | ||
"description": "The PaymentMethod received from stripe.com" | ||
}, | ||
{ | ||
"name": "card-changed", | ||
"description": "The new value of card" | ||
"name": "source", | ||
"description": "The Source received from stripe.com" | ||
}, | ||
{ | ||
"name": "is-complete-changed", | ||
"description": "The new value of is-complete" | ||
"name": "token", | ||
"description": "The Token received from stripe.com" | ||
}, | ||
{ | ||
"name": "is-empty-changed", | ||
"description": "The new value of is-empty" | ||
"name": "success", | ||
"description": "When a payment succeeds" | ||
}, | ||
{ | ||
"name": "stripe-ready-changed", | ||
"description": "The new value of stripe-ready" | ||
"name": "ready", | ||
"description": "Stripe has been initialized and mounted" | ||
}, | ||
{ | ||
"name": "stripe-error", | ||
"description": "The validation error, or the error returned from stripe.com" | ||
"name": "stripe-ready", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `ready` instead" | ||
}, | ||
{ | ||
"name": "stripe-payment-intent", | ||
"description": "The PaymentIntent received from stripe.com" | ||
"name": "stripe-error", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `error` instead" | ||
}, | ||
{ | ||
"name": "stripe-payment-method", | ||
"description": "The PaymentMethod received from stripe.com" | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `payment-method` instead" | ||
}, | ||
{ | ||
"name": "stripe-source", | ||
"description": "The Source received from stripe.com" | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `source` instead" | ||
}, | ||
{ | ||
"name": "stripe-token", | ||
"description": "The Token received from stripe.com" | ||
}, | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `token` instead" | ||
} | ||
], | ||
"cssProperties": [ | ||
{ | ||
"name": "error-changed", | ||
"description": "The new value of error" | ||
"name": "--stripe-elements-border-radius", | ||
"description": "border radius of the element container. Default `4px`" | ||
}, | ||
{ | ||
"name": "has-error-changed", | ||
"description": "The new value of has-error" | ||
"name": "--stripe-elements-border", | ||
"description": "border property of the element container. Default `1px solid transparent`" | ||
}, | ||
{ | ||
"name": "payment-intent-changed", | ||
"description": "The new value of payment-intent" | ||
"name": "--stripe-elements-box-shadow", | ||
"description": "box shadow for the element container. Default `0 1px 3px 0 #e6ebf1`" | ||
}, | ||
{ | ||
"name": "payment-method-changed", | ||
"description": "The new value of payment-method" | ||
"name": "--stripe-elements-transition", | ||
"description": "transition property for the element container. Default `box-shadow 150ms ease`" | ||
}, | ||
{ | ||
"name": "publishable-key-changed", | ||
"description": "The new value of publishable-key" | ||
}, | ||
{ | ||
"name": "source-changed", | ||
"description": "The new value of source" | ||
}, | ||
{ | ||
"name": "token-changed", | ||
"description": "The new value of token" | ||
} | ||
], | ||
"cssProperties": [ | ||
{ | ||
"name": "--stripe-elements-base-color", | ||
@@ -564,5 +638,508 @@ "description": "`color` property for the element in its base state" | ||
} | ||
], | ||
"cssParts": [ | ||
{ | ||
"name": "error", | ||
"description": "container for the error message" | ||
}, | ||
{ | ||
"name": "stripe", | ||
"description": "container for the stripe element" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "stripe-payment-request", | ||
"description": "Custom element wrapper for Stripe.js v3 Payment Request Buttons.\n\n👨🎨 [Live Demo](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-payment-request--enter-a-stripe-publishable-key) 👀\n\n### 🧙♂️ Usage\nIf you prebuilt with Snowpack, load the module from your `web_modules` directory\n\n```html\n<script type=\"module\" src=\"/web_modules/@power-elements/stripe-elements/stripe-payment-request.js\"></script>\n```\n\nAlternatively, load the module from the unpkg CDN\n```html\n<script type=\"module\" src=\"https://unpkg.com/@power-elements/stripe-elements/stripe-payment-request.js?module\"></script>\n```\n\nThen you can add the element to your page.\n\n```html\n\n<stripe-payment-request id=\"payment-request\"\n publishable-key=\"pk_test_XXXXXXXXXXXXXXXXXXXXXXXX\"\n generate=\"token\"\n action=\"/charges\"\n country=\"CA\"\n currency=\"cad\"\n amount=\"1000\"\n label=\"Ten Bones\"\n request-payer-name\n request-payer-email\n request-payer-phone\n></stripe-payment-request>\n```\n\nSee the demos for more comprehensive examples.\n - Using `<stripe-payment-request>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vanilla--stripe-payment-request).\n - Using `<stripe-payment-request>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-litelement--stripe-payment-request).\n - Using `<stripe-payment-request>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vue--stripe-payment-request).\n - Using `<stripe-payment-request>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-angular--stripe-payment-request).\n - Using `<stripe-payment-request>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-react--stripe-payment-request).\n - Using `<stripe-payment-request>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-preact--stripe-payment-request).", | ||
"attributes": [ | ||
{ | ||
"name": "amount", | ||
"description": "The amount in the currency's subunit (e.g. cents, yen, etc.)", | ||
"type": "number" | ||
}, | ||
{ | ||
"name": "can-make-payment", | ||
"description": "Whether or not the device can make the payment request.", | ||
"type": "object" | ||
}, | ||
{ | ||
"name": "country", | ||
"description": "The two-letter country code of your Stripe account (e.g., `US`)", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "currency", | ||
"description": "Three character currency code (e.g., `usd`)", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "displayItems", | ||
"description": "An array of DisplayItem objects. These objects are shown as line items in the browser’s payment interface. Note that the sum of the line item amounts does not need to add up to the total amount above.", | ||
"type": "stripe.paymentRequest.DisplayItem[]" | ||
}, | ||
{ | ||
"name": "label", | ||
"description": "A name that the browser shows the customer in the payment interface.", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "payment-intent", | ||
"description": "Stripe PaymentIntent", | ||
"type": "stripe.paymentIntents.PaymentIntent" | ||
}, | ||
{ | ||
"name": "payment-request", | ||
"description": "Stripe PaymentRequest", | ||
"type": "stripe.paymentRequest.StripePaymentRequest" | ||
}, | ||
{ | ||
"name": "pending", | ||
"description": "If you might change the payment amount later (for example, after you have calcluated shipping costs), set this to true. Note that browsers treat this as a hint for how to display things, and not necessarily as something that will prevent submission.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "request-payer-email", | ||
"description": "See the requestPayerName option.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "request-payer-name", | ||
"description": "By default, the browser‘s payment interface only asks the customer for actual payment information. A customer name can be collected by setting this option to true. This collected name will appears in the PaymentResponse object.\n\nWe highly recommend you collect at least one of name, email, or phone as this also results in collection of billing address for Apple Pay. The billing address can be used to perform address verification and block fraudulent payments. For all other payment methods, the billing address is automatically collected when available.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "request-payer-phone", | ||
"description": "See the requestPayerName option.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "request-shipping", | ||
"description": "Collect shipping address by setting this option to true. The address appears in the PaymentResponse.\nYou must also supply a valid [ShippingOptions] to the shippingOptions property. This can be up front at the time stripe.paymentRequest is called, or in response to a shippingaddresschange event using the updateWith callback.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "shippingOptions", | ||
"description": "An array of ShippingOption objects. The first shipping option listed appears in the browser payment interface as the default option.", | ||
"type": "stripe.paymentRequest.ShippingOption[]" | ||
}, | ||
{ | ||
"name": "button-type", | ||
"type": "'default'|'book'|'buy'|'donate'", | ||
"default": "\"default\"" | ||
}, | ||
{ | ||
"name": "button-theme", | ||
"type": "'dark'|'light'|'light-outline'", | ||
"default": "\"dark\"" | ||
}, | ||
{ | ||
"name": "payment-method", | ||
"description": "Stripe PaymentMethod", | ||
"type": "stripe.paymentMethod.PaymentMethod" | ||
}, | ||
{ | ||
"name": "source", | ||
"description": "Stripe Source", | ||
"type": "stripe.Source" | ||
}, | ||
{ | ||
"name": "token", | ||
"description": "Stripe Token", | ||
"type": "stripe.Token" | ||
}, | ||
{ | ||
"name": "action", | ||
"description": "If set, when Stripe returns the payment info (PaymentMethod, Source, or Token),\nthe element will POST JSON data to this URL with an object containing\na key equal to the value of the `generate` property.", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "client-secret", | ||
"description": "The `client_secret` part of a Stripe `PaymentIntent`", | ||
"type": "String" | ||
}, | ||
{ | ||
"name": "generate", | ||
"description": "Type of payment representation to generate.", | ||
"type": "'payment-method'|'source'|'token'", | ||
"default": "\"source\"" | ||
}, | ||
{ | ||
"name": "publishable-key", | ||
"description": "Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX`", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "show-error", | ||
"description": "Whether to display the error message", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "element", | ||
"description": "Stripe element instance", | ||
"type": "stripe.elements.Element" | ||
}, | ||
{ | ||
"name": "elements", | ||
"description": "Stripe Elements instance", | ||
"type": "stripe.elements.Elements" | ||
}, | ||
{ | ||
"name": "error", | ||
"description": "Stripe or validation error", | ||
"type": "Error|stripe.Error" | ||
}, | ||
{ | ||
"name": "focused", | ||
"description": "If the element is focused.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "ready", | ||
"description": "Whether the stripe element is ready to receive focus.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "stripe", | ||
"description": "Stripe instance", | ||
"type": "stripe.Stripe" | ||
}, | ||
{ | ||
"name": "has-error", | ||
"description": "Whether the element has an error\n**DEPRECATED**. Will be removed in a future version. Use `error` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripe-ready", | ||
"description": "Whether the stripe element is ready to receive focus.\n**DEPRECATED**. Will be removed in a future version. use `ready` instead.", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
} | ||
], | ||
"properties": [ | ||
{ | ||
"name": "amount", | ||
"attribute": "amount", | ||
"description": "The amount in the currency's subunit (e.g. cents, yen, etc.)", | ||
"type": "number" | ||
}, | ||
{ | ||
"name": "canMakePayment", | ||
"attribute": "can-make-payment", | ||
"description": "Whether or not the device can make the payment request.", | ||
"type": "object" | ||
}, | ||
{ | ||
"name": "country", | ||
"attribute": "country", | ||
"description": "The two-letter country code of your Stripe account (e.g., `US`)", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "currency", | ||
"attribute": "currency", | ||
"description": "Three character currency code (e.g., `usd`)", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "displayItems", | ||
"attribute": "displayItems", | ||
"description": "An array of DisplayItem objects. These objects are shown as line items in the browser’s payment interface. Note that the sum of the line item amounts does not need to add up to the total amount above.", | ||
"type": "stripe.paymentRequest.DisplayItem[]" | ||
}, | ||
{ | ||
"name": "label", | ||
"attribute": "label", | ||
"description": "A name that the browser shows the customer in the payment interface.", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "paymentIntent", | ||
"attribute": "payment-intent", | ||
"description": "Stripe PaymentIntent", | ||
"type": "stripe.paymentIntents.PaymentIntent" | ||
}, | ||
{ | ||
"name": "paymentRequest", | ||
"attribute": "payment-request", | ||
"description": "Stripe PaymentRequest", | ||
"type": "stripe.paymentRequest.StripePaymentRequest" | ||
}, | ||
{ | ||
"name": "pending", | ||
"attribute": "pending", | ||
"description": "If you might change the payment amount later (for example, after you have calcluated shipping costs), set this to true. Note that browsers treat this as a hint for how to display things, and not necessarily as something that will prevent submission.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "requestPayerEmail", | ||
"attribute": "request-payer-email", | ||
"description": "See the requestPayerName option.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "requestPayerName", | ||
"attribute": "request-payer-name", | ||
"description": "By default, the browser‘s payment interface only asks the customer for actual payment information. A customer name can be collected by setting this option to true. This collected name will appears in the PaymentResponse object.\n\nWe highly recommend you collect at least one of name, email, or phone as this also results in collection of billing address for Apple Pay. The billing address can be used to perform address verification and block fraudulent payments. For all other payment methods, the billing address is automatically collected when available.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "requestPayerPhone", | ||
"attribute": "request-payer-phone", | ||
"description": "See the requestPayerName option.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "requestShipping", | ||
"attribute": "request-shipping", | ||
"description": "Collect shipping address by setting this option to true. The address appears in the PaymentResponse.\nYou must also supply a valid [ShippingOptions] to the shippingOptions property. This can be up front at the time stripe.paymentRequest is called, or in response to a shippingaddresschange event using the updateWith callback.", | ||
"type": "boolean" | ||
}, | ||
{ | ||
"name": "shippingOptions", | ||
"attribute": "shippingOptions", | ||
"description": "An array of ShippingOption objects. The first shipping option listed appears in the browser payment interface as the default option.", | ||
"type": "stripe.paymentRequest.ShippingOption[]" | ||
}, | ||
{ | ||
"name": "buttonType", | ||
"attribute": "button-type", | ||
"type": "'default'|'book'|'buy'|'donate'", | ||
"default": "\"default\"" | ||
}, | ||
{ | ||
"name": "buttonTheme", | ||
"attribute": "button-theme", | ||
"type": "'dark'|'light'|'light-outline'", | ||
"default": "\"dark\"" | ||
}, | ||
{ | ||
"name": "billingDetails", | ||
"description": "billing_details object sent to create the payment representation. (optional)", | ||
"type": "stripe.BillingDetails", | ||
"default": "{}" | ||
}, | ||
{ | ||
"name": "paymentMethodData", | ||
"description": "Data passed to stripe.createPaymentMethod. (optional)", | ||
"type": "stripe.PaymentMethodData", | ||
"default": "{}" | ||
}, | ||
{ | ||
"name": "sourceData", | ||
"description": "Data passed to stripe.createSource. (optional)", | ||
"type": "SourceData", | ||
"default": "{}" | ||
}, | ||
{ | ||
"name": "tokenData", | ||
"description": "Data passed to stripe.createToken. (optional)", | ||
"type": "stripe.TokenOptions", | ||
"default": "{}" | ||
}, | ||
{ | ||
"name": "paymentMethod", | ||
"attribute": "payment-method", | ||
"description": "Stripe PaymentMethod", | ||
"type": "stripe.paymentMethod.PaymentMethod" | ||
}, | ||
{ | ||
"name": "source", | ||
"attribute": "source", | ||
"description": "Stripe Source", | ||
"type": "stripe.Source" | ||
}, | ||
{ | ||
"name": "token", | ||
"attribute": "token", | ||
"description": "Stripe Token", | ||
"type": "stripe.Token" | ||
}, | ||
{ | ||
"name": "action", | ||
"attribute": "action", | ||
"description": "If set, when Stripe returns the payment info (PaymentMethod, Source, or Token),\nthe element will POST JSON data to this URL with an object containing\na key equal to the value of the `generate` property.", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "clientSecret", | ||
"attribute": "client-secret", | ||
"description": "The `client_secret` part of a Stripe `PaymentIntent`", | ||
"type": "String" | ||
}, | ||
{ | ||
"name": "generate", | ||
"attribute": "generate", | ||
"description": "Type of payment representation to generate.", | ||
"type": "'payment-method'|'source'|'token'", | ||
"default": "\"source\"" | ||
}, | ||
{ | ||
"name": "publishableKey", | ||
"attribute": "publishable-key", | ||
"description": "Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX`", | ||
"type": "string" | ||
}, | ||
{ | ||
"name": "showError", | ||
"attribute": "show-error", | ||
"description": "Whether to display the error message", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "element", | ||
"attribute": "element", | ||
"description": "Stripe element instance", | ||
"type": "stripe.elements.Element" | ||
}, | ||
{ | ||
"name": "elements", | ||
"attribute": "elements", | ||
"description": "Stripe Elements instance", | ||
"type": "stripe.elements.Elements" | ||
}, | ||
{ | ||
"name": "error", | ||
"attribute": "error", | ||
"description": "Stripe or validation error", | ||
"type": "Error|stripe.Error" | ||
}, | ||
{ | ||
"name": "focused", | ||
"attribute": "focused", | ||
"description": "If the element is focused.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "ready", | ||
"attribute": "ready", | ||
"description": "Whether the stripe element is ready to receive focus.", | ||
"type": "boolean", | ||
"default": "false" | ||
}, | ||
{ | ||
"name": "stripe", | ||
"attribute": "stripe", | ||
"description": "Stripe instance", | ||
"type": "stripe.Stripe" | ||
}, | ||
{ | ||
"name": "hasError", | ||
"attribute": "has-error", | ||
"description": "Whether the element has an error\n**DEPRECATED**. Will be removed in a future version. Use `error` instead", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripeReady", | ||
"attribute": "stripe-ready", | ||
"description": "Whether the stripe element is ready to receive focus.\n**DEPRECATED**. Will be removed in a future version. use `ready` instead.", | ||
"type": "boolean", | ||
"default": "false", | ||
"deprecated": true | ||
}, | ||
{ | ||
"name": "stripeMount", | ||
"description": "Stripe.js mount point element. Due to limitations in the Stripe.js library, this element must be connected to the document.", | ||
"type": "Element" | ||
} | ||
], | ||
"events": [ | ||
{ | ||
"name": "fail", | ||
"description": "When a payment request fails" | ||
}, | ||
{ | ||
"name": "cancel", | ||
"description": "When a payment request is cancelled" | ||
}, | ||
{ | ||
"name": "shippingaddresschange", | ||
"description": "When the user chooses a different shipping address" | ||
}, | ||
{ | ||
"name": "shippingoptionchange", | ||
"description": "When the user chooses a different shipping option" | ||
}, | ||
{ | ||
"name": "error", | ||
"description": "The validation error, or the error returned from stripe.com" | ||
}, | ||
{ | ||
"name": "payment-method", | ||
"description": "The PaymentMethod received from stripe.com" | ||
}, | ||
{ | ||
"name": "source", | ||
"description": "The Source received from stripe.com" | ||
}, | ||
{ | ||
"name": "token", | ||
"description": "The Token received from stripe.com" | ||
}, | ||
{ | ||
"name": "success", | ||
"description": "When a payment succeeds" | ||
}, | ||
{ | ||
"name": "ready", | ||
"description": "Stripe has been initialized and mounted" | ||
}, | ||
{ | ||
"name": "stripe-ready", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `ready` instead" | ||
}, | ||
{ | ||
"name": "stripe-error", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `error` instead" | ||
}, | ||
{ | ||
"name": "stripe-payment-method", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `payment-method` instead" | ||
}, | ||
{ | ||
"name": "stripe-source", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `source` instead" | ||
}, | ||
{ | ||
"name": "stripe-token", | ||
"description": "**DEPRECATED**. Will be removed in a future major version. Use `token` instead" | ||
} | ||
], | ||
"cssProperties": [ | ||
{ | ||
"name": "--stripe-payment-request-element-min-width", | ||
"description": "min-width property of the container element. Default `300px`" | ||
}, | ||
{ | ||
"name": "--stripe-payment-request-element-padding", | ||
"description": "padding property of the container element. Default `8px 12px`" | ||
}, | ||
{ | ||
"name": "--stripe-payment-request-element-background", | ||
"description": "background property of the container element. Default `white`" | ||
} | ||
], | ||
"cssParts": [ | ||
{ | ||
"name": "error", | ||
"description": "container for the error message" | ||
}, | ||
{ | ||
"name": "stripe", | ||
"description": "container for the stripe element" | ||
} | ||
] | ||
} | ||
] | ||
} |
@@ -29,4 +29,14 @@ /* eslint-disable valid-jsdoc */ | ||
}, | ||
coverageIstanbulReporter: { | ||
thresholds: { | ||
global: { | ||
statements: 100, | ||
lines: 100, | ||
branches: 100, | ||
functions: 100, | ||
}, | ||
}, | ||
}, | ||
})); | ||
return config; | ||
}; |
{ | ||
"name": "@power-elements/stripe-elements", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"description": "Web Component wrapper for stripe elements", | ||
@@ -69,4 +69,4 @@ "main": "stripe-elements.js", | ||
"@material/mwc-textfield": "^0.12.0", | ||
"@open-wc/demoing-storybook": "^1.6.4", | ||
"@open-wc/testing": "^2.5.0", | ||
"@open-wc/demoing-storybook": "^1.8.5", | ||
"@open-wc/testing": "^2.5.1", | ||
"@open-wc/testing-karma": "^3.2.30", | ||
@@ -91,3 +91,3 @@ "@power-elements/codesandbox-button": "0.0.2", | ||
"eslint-plugin-no-only-tests": "^2.4.0", | ||
"husky": "^4.0.10", | ||
"husky": "^4.2.1", | ||
"karma-helpful-reporter": "^0.3.4", | ||
@@ -99,6 +99,6 @@ "karma-osx-reporter": "^0.2.1", | ||
"patch-package": "^6.2.0", | ||
"rollup": "^1.29.0", | ||
"rollup": "^1.29.1", | ||
"rollup-plugin-babel": "^4.3.3", | ||
"rollup-plugin-lit-css": "^2.0.0", | ||
"sinon": "^8.0.4", | ||
"sinon": "^8.1.1", | ||
"sinon-chai": "^3.4.0", | ||
@@ -115,3 +115,3 @@ "stylelint-config-standard": "^19.0.0", | ||
"@morbidick/lit-element-notify": "^1.0.2", | ||
"@open-wc/lit-helpers": "^0.2.5", | ||
"@open-wc/lit-helpers": "^0.2.6", | ||
"@pacote/memoize": "^1.1.1", | ||
@@ -118,0 +118,0 @@ "@typed/curry": "^1.0.1", |
233
README.md
# Stripe Elements Web Components | ||
Custom element wrappers for Stripe.js v3 Elements. | ||
🛡⚛️🔰 **Any** Framework - **One** Stripe Integration. 💰💵💸 | ||
[](https://www.npmjs.com/package/@power-elements/stripe-elements) | ||
[](https://www.webcomponents.org/element/bennypowers/stripe-elements) | ||
[](https://open-wc.org) | ||
[](https://www.webcomponents.org/element/bennypowers/stripe-elements) | ||
[](https://www.npmjs.com/package/@power-elements/stripe-elements) | ||
[](https://codeclimate.com/github/bennypowers/stripe-elements/maintainability) | ||
@@ -63,3 +63,2 @@ [](https://codeclimate.com/github/bennypowers/stripe-elements/test_coverage) | ||
```html | ||
<script type="module" src="https://unpkg.com/@power-elements/stripe-elements/stripe-elements.js?module"></script> | ||
<stripe-elements id="stripe" | ||
@@ -72,9 +71,8 @@ action="/payment" | ||
See the demos for more comprehensive examples. | ||
- Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-plain-html-and-javascript). | ||
- Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-lit-element). | ||
- Using `<stripe-elements>` in a [Polymer Element](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-polymer-element). | ||
- Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-vue-component). | ||
- Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-an-angular-component). | ||
- Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-react-component). | ||
- Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-preact-component). | ||
- Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-html--stripe-elements). | ||
- Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-litelement--stripe-elements). | ||
- Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vue--stripe-elements). | ||
- Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-angular--stripe-elements). | ||
- Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-react--stripe-elements). | ||
- Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-preact--stripe-elements). | ||
@@ -117,17 +115,23 @@ ## Styling | ||
| `billingDetails` | | | `stripe.BillingDetails` | {} | billing_details object sent to create the payment representation. (optional) | | ||
| `brand` | `brand` | readonly | `String` | null | The card brand detected by Stripe | | ||
| `card` | `card` | readonly | `stripe.Element` | null | The Stripe card object. | | ||
| `brand` | `brand` | readonly | `string` | null | The card brand detected by Stripe | | ||
| `card` | `card` | readonly | `stripe.elements.Element` | null | The Stripe card object.<br />**DEPRECATED**. Will be removed in a future version. use `element` instead | | ||
| `clientSecret` | `client-secret` | | `String` | | The `client_secret` part of a Stripe `PaymentIntent` | | ||
| `complete` | `complete` | readonly | `boolean` | false | Whether the form is complete. | | ||
| `element` | `element` | readonly | `stripe.elements.Element` | null | Stripe element instance | | ||
| `elements` | `elements` | readonly | `stripe.elements.Elements` | null | Stripe Elements instance | | ||
| `empty` | `empty` | readonly | `boolean` | true | If the form is empty. | | ||
| `error` | `error` | readonly | `Error\|stripe.Error` | null | Stripe or validation error | | ||
| `focused` | `focused` | readonly | `boolean` | false | If the element is focused. | | ||
| `generate` | `generate` | | `'payment-method'\|'source'\|'token'` | "source" | Type of payment representation to generate. | | ||
| `hasError` | `has-error` | readonly | `Boolean` | false | Whether the element has an error | | ||
| `hideIcon` | `hide-icon` | | `Boolean` | false | Whether to hide icons in the Stripe form. | | ||
| `hidePostalCode` | `hide-postal-code` | | `Boolean` | false | Whether or not to hide the postal code field.<br />Useful when you gather shipping info elsewhere. | | ||
| `hasError` | `has-error` | readonly | `boolean` | false | Whether the element has an error<br />**DEPRECATED**. Will be removed in a future version. Use `error` instead | | ||
| `hideIcon` | `hide-icon` | | `boolean` | false | Whether to hide icons in the Stripe form. | | ||
| `hidePostalCode` | `hide-postal-code` | | `boolean` | false | Whether or not to hide the postal code field.<br />Useful when you gather shipping info elsewhere. | | ||
| `iconStyle` | `icon-style` | | `'solid'\|'default'` | "default" | Stripe icon style. 'solid' or 'default'. | | ||
| `isComplete` | `is-complete` | | `Boolean` | false | If the form is complete. | | ||
| `isEmpty` | `is-empty` | | `Boolean` | true | If the form is empty. | | ||
| `paymentMethod` | `payment-method` | readonly | `stripe.PaymentMethod` | null | Stripe PaymentMethod | | ||
| `invalid` | `invalid` | readonly | `boolean` | false | Whether the form is invalid. | | ||
| `isComplete` | `is-complete` | | `boolean` | false | Whether the form is complete.<br />**DEPRECATED**. Will be removed in a future version. use `complete` instead | | ||
| `isEmpty` | `is-empty` | | `boolean` | true | Whether the form is empty.<br />**DEPRECATED**. Will be removed in a future version. use `empty` instead | | ||
| `paymentMethod` | `payment-method` | readonly | `stripe.paymentMethod.PaymentMethod` | null | Stripe PaymentMethod | | ||
| `paymentMethodData` | | | `stripe.PaymentMethodData` | {} | Data passed to stripe.createPaymentMethod. (optional) | | ||
| `publishableKey` | `publishable-key` | | `String` | | Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX` | | ||
| `publishableKey` | `publishable-key` | | `string` | | Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX` | | ||
| `ready` | `ready` | readonly | `boolean` | false | Whether the stripe element is ready to receive focus. | | ||
| `showError` | `show-error` | | `boolean` | false | Whether to display the error message | | ||
@@ -137,7 +141,7 @@ | `source` | `source` | readonly | `stripe.Source` | null | Stripe Source | | ||
| `stripe` | `stripe` | readonly | `stripe.Stripe` | null | Stripe instance | | ||
| `stripeMount` | | readonly | `Element` | | Stripe Element mount point | | ||
| `stripeReady` | `stripe-ready` | | `Boolean` | false | If the stripe element is ready to receive focus. | | ||
| `stripeMount` | | readonly | `Element` | | Stripe.js mount point element. Due to limitations in the Stripe.js library, this element must be connected to the document. | | ||
| `stripeReady` | `stripe-ready` | | `boolean` | false | Whether the stripe element is ready to receive focus.<br />**DEPRECATED**. Will be removed in a future version. use `ready` instead. | | ||
| `token` | `token` | readonly | `stripe.Token` | null | Stripe Token | | ||
| `tokenData` | | | `stripe.TokenOptions` | {} | Data passed to stripe.createToken. (optional) | | ||
| `value` | `value` | | `Object` | {} | Prefilled values for form. Example {postalCode: '90210'} | | ||
| `value` | `value` | | `object` | {} | Prefilled values for form. Example {postalCode: '90210'} | | ||
@@ -148,34 +152,36 @@ #### Methods | ||
|-----------------------|--------------------------------------------------|--------------------------------------------------| | ||
| `blur` | `(): void` | Blur the element. | | ||
| `createPaymentMethod` | `(paymentMethodData?: PaymentMethodData \| undefined): Promise<PaymentMethodResponse>` | Submit payment information to generate a paymentMethod | | ||
| `createSource` | `(sourceData?: { owner: OwnerInfo; } \| undefined): Promise<SourceResponse>` | Submit payment information to generate a source | | ||
| `createToken` | `(tokenData?: TokenData): Promise<TokenResponse>` | Submit payment information to generate a token | | ||
| `isPotentiallyValid` | `(): Boolean` | Checks for potential validity. A potentially valid form is one that is not empty, not complete and has no error. A validated form also counts as potentially valid. | | ||
| `focus` | `(): void` | Focus the element. | | ||
| `isPotentiallyValid` | `(): boolean` | Checks for potential validity. A potentially valid form is one that is not empty, not complete and has no error. A validated form also counts as potentially valid. | | ||
| `reset` | `(): void` | Resets the Stripe card. | | ||
| `submit` | `(): Promise<PaymentMethodResponse \| SourceResponse \| TokenResponse>` | Generates a payment representation of the type specified by `generate`. | | ||
| `validate` | `(): Boolean` | Checks if the Stripe form is valid. | | ||
| `validate` | `(): boolean` | Checks if the Stripe form is valid. | | ||
#### Events | ||
| Event | Description | | ||
|---------------------------|--------------------------------------------------| | ||
| `brand-changed` | The new value of brand | | ||
| `card-changed` | The new value of card | | ||
| `error-changed` | The new value of error | | ||
| `has-error-changed` | The new value of has-error | | ||
| `is-complete-changed` | The new value of is-complete | | ||
| `is-empty-changed` | The new value of is-empty | | ||
| `payment-intent-changed` | The new value of payment-intent | | ||
| `payment-method-changed` | The new value of payment-method | | ||
| `publishable-key-changed` | The new value of publishable-key | | ||
| `source-changed` | The new value of source | | ||
| `stripe-change` | Stripe Element change event | | ||
| `stripe-error` | The validation error, or the error returned from stripe.com | | ||
| `stripe-payment-intent` | The PaymentIntent received from stripe.com | | ||
| `stripe-payment-method` | The PaymentMethod received from stripe.com | | ||
| `stripe-ready` | Stripe has been initialized and mounted | | ||
| `stripe-ready-changed` | The new value of stripe-ready | | ||
| `stripe-source` | The Source received from stripe.com | | ||
| `stripe-token` | The Token received from stripe.com | | ||
| `token-changed` | The new value of token | | ||
| Event | Description | | ||
|-------------------------|--------------------------------------------------| | ||
| `change` | Stripe Element change event | | ||
| `error` | The validation error, or the error returned from stripe.com | | ||
| `payment-method` | The PaymentMethod received from stripe.com | | ||
| `ready` | Stripe has been initialized and mounted | | ||
| `source` | The Source received from stripe.com | | ||
| `stripe-error` | **DEPRECATED**. Will be removed in a future major version. Use `error` instead | | ||
| `stripe-payment-method` | **DEPRECATED**. Will be removed in a future major version. Use `payment-method` instead | | ||
| `stripe-ready` | **DEPRECATED**. Will be removed in a future major version. Use `ready` instead | | ||
| `stripe-source` | **DEPRECATED**. Will be removed in a future major version. Use `source` instead | | ||
| `stripe-token` | **DEPRECATED**. Will be removed in a future major version. Use `token` instead | | ||
| `success` | When a payment succeeds | | ||
| `token` | The Token received from stripe.com | | ||
#### CSS Shadow Parts | ||
| Part | Description | | ||
|----------|----------------------------------| | ||
| `error` | container for the error message | | ||
| `stripe` | container for the stripe element | | ||
#### CSS Custom Properties | ||
@@ -196,2 +202,5 @@ | ||
| `--stripe-elements-base-text-transform` | `text-transform` property for the element in its base state | | ||
| `--stripe-elements-border` | border property of the element container. Default `1px solid transparent` | | ||
| `--stripe-elements-border-radius` | border radius of the element container. Default `4px` | | ||
| `--stripe-elements-box-shadow` | box shadow for the element container. Default `0 1px 3px 0 #e6ebf1` | | ||
| `--stripe-elements-complete-color` | `color` property for the element in its complete state | | ||
@@ -230,2 +239,134 @@ | `--stripe-elements-complete-font-family` | `font-family` property for the element in its complete state | | ||
| `--stripe-elements-invalid-text-transform` | `text-transform` property for the element in its invalid state | | ||
| `--stripe-elements-transition` | transition property for the element container. Default `box-shadow 150ms ease` | | ||
### stripe-payment-request | ||
Custom element wrapper for Stripe.js v3 Payment Request Buttons. | ||
👨🎨 [Live Demo](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-payment-request--enter-a-stripe-publishable-key) 👀 | ||
### 🧙♂️ Usage | ||
If you prebuilt with Snowpack, load the module from your `web_modules` directory | ||
```html | ||
<script type="module" src="/web_modules/@power-elements/stripe-elements/stripe-payment-request.js"></script> | ||
``` | ||
Alternatively, load the module from the unpkg CDN | ||
```html | ||
<script type="module" src="https://unpkg.com/@power-elements/stripe-elements/stripe-payment-request.js?module"></script> | ||
``` | ||
Then you can add the element to your page. | ||
```html | ||
<stripe-payment-request id="payment-request" | ||
publishable-key="pk_test_XXXXXXXXXXXXXXXXXXXXXXXX" | ||
generate="token" | ||
action="/charges" | ||
country="CA" | ||
currency="cad" | ||
amount="1000" | ||
label="Ten Bones" | ||
request-payer-name | ||
request-payer-email | ||
request-payer-phone | ||
></stripe-payment-request> | ||
``` | ||
See the demos for more comprehensive examples. | ||
- Using `<stripe-payment-request>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vanilla--stripe-payment-request). | ||
- Using `<stripe-payment-request>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-litelement--stripe-payment-request). | ||
- Using `<stripe-payment-request>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vue--stripe-payment-request). | ||
- Using `<stripe-payment-request>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-angular--stripe-payment-request). | ||
- Using `<stripe-payment-request>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-react--stripe-payment-request). | ||
- Using `<stripe-payment-request>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-preact--stripe-payment-request). | ||
**Mixins:** ReadOnlyPropertiesMixin, LitNotify | ||
#### Properties | ||
| Property | Attribute | Modifiers | Type | Default | Description | | ||
|---------------------|-----------------------|-----------|----------------------------------------------|-----------|--------------------------------------------------| | ||
| `action` | `action` | | `string` | | If set, when Stripe returns the payment info (PaymentMethod, Source, or Token),<br />the element will POST JSON data to this URL with an object containing<br />a key equal to the value of the `generate` property. | | ||
| `amount` | `amount` | | `number` | | The amount in the currency's subunit (e.g. cents, yen, etc.) | | ||
| `billingDetails` | | | `stripe.BillingDetails` | {} | billing_details object sent to create the payment representation. (optional) | | ||
| `buttonTheme` | `button-theme` | | `'dark'\|'light'\|'light-outline'` | "dark" | | | ||
| `buttonType` | `button-type` | | `'default'\|'book'\|'buy'\|'donate'` | "default" | | | ||
| `canMakePayment` | `can-make-payment` | readonly | `object` | null | Whether or not the device can make the payment request. | | ||
| `clientSecret` | `client-secret` | | `String` | | The `client_secret` part of a Stripe `PaymentIntent` | | ||
| `country` | `country` | | `string` | | The two-letter country code of your Stripe account (e.g., `US`) | | ||
| `currency` | `currency` | | `string` | | Three character currency code (e.g., `usd`) | | ||
| `displayItems` | `displayItems` | | `stripe.paymentRequest.DisplayItem[]` | | An array of DisplayItem objects. These objects are shown as line items in the browser’s payment interface. Note that the sum of the line item amounts does not need to add up to the total amount above. | | ||
| `element` | `element` | readonly | `stripe.elements.Element` | null | Stripe element instance | | ||
| `elements` | `elements` | readonly | `stripe.elements.Elements` | null | Stripe Elements instance | | ||
| `error` | `error` | readonly | `Error\|stripe.Error` | null | Stripe or validation error | | ||
| `focused` | `focused` | readonly | `boolean` | false | If the element is focused. | | ||
| `generate` | `generate` | | `'payment-method'\|'source'\|'token'` | "source" | Type of payment representation to generate. | | ||
| `hasError` | `has-error` | readonly | `boolean` | false | Whether the element has an error<br />**DEPRECATED**. Will be removed in a future version. Use `error` instead | | ||
| `label` | `label` | | `string` | | A name that the browser shows the customer in the payment interface. | | ||
| `paymentIntent` | `payment-intent` | readonly | `stripe.paymentIntents.PaymentIntent` | null | Stripe PaymentIntent | | ||
| `paymentMethod` | `payment-method` | readonly | `stripe.paymentMethod.PaymentMethod` | null | Stripe PaymentMethod | | ||
| `paymentMethodData` | | | `stripe.PaymentMethodData` | {} | Data passed to stripe.createPaymentMethod. (optional) | | ||
| `paymentRequest` | `payment-request` | | `stripe.paymentRequest.StripePaymentRequest` | null | Stripe PaymentRequest | | ||
| `pending` | `pending` | | `boolean` | false | If you might change the payment amount later (for example, after you have calcluated shipping costs), set this to true. Note that browsers treat this as a hint for how to display things, and not necessarily as something that will prevent submission. | | ||
| `publishableKey` | `publishable-key` | | `string` | | Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX` | | ||
| `ready` | `ready` | readonly | `boolean` | false | Whether the stripe element is ready to receive focus. | | ||
| `requestPayerEmail` | `request-payer-email` | | `boolean` | | See the requestPayerName option. | | ||
| `requestPayerName` | `request-payer-name` | | `boolean` | | By default, the browser‘s payment interface only asks the customer for actual payment information. A customer name can be collected by setting this option to true. This collected name will appears in the PaymentResponse object.<br /><br />We highly recommend you collect at least one of name, email, or phone as this also results in collection of billing address for Apple Pay. The billing address can be used to perform address verification and block fraudulent payments. For all other payment methods, the billing address is automatically collected when available. | | ||
| `requestPayerPhone` | `request-payer-phone` | | `boolean` | | See the requestPayerName option. | | ||
| `requestShipping` | `request-shipping` | | `boolean` | | Collect shipping address by setting this option to true. The address appears in the PaymentResponse.<br />You must also supply a valid [ShippingOptions] to the shippingOptions property. This can be up front at the time stripe.paymentRequest is called, or in response to a shippingaddresschange event using the updateWith callback. | | ||
| `shippingOptions` | `shippingOptions` | | `stripe.paymentRequest.ShippingOption[]` | | An array of ShippingOption objects. The first shipping option listed appears in the browser payment interface as the default option. | | ||
| `showError` | `show-error` | | `boolean` | false | Whether to display the error message | | ||
| `source` | `source` | readonly | `stripe.Source` | null | Stripe Source | | ||
| `sourceData` | | | `SourceData` | {} | Data passed to stripe.createSource. (optional) | | ||
| `stripe` | `stripe` | readonly | `stripe.Stripe` | null | Stripe instance | | ||
| `stripeMount` | | readonly | `Element` | | Stripe.js mount point element. Due to limitations in the Stripe.js library, this element must be connected to the document. | | ||
| `stripeReady` | `stripe-ready` | | `boolean` | false | Whether the stripe element is ready to receive focus.<br />**DEPRECATED**. Will be removed in a future version. use `ready` instead. | | ||
| `token` | `token` | readonly | `stripe.Token` | null | Stripe Token | | ||
| `tokenData` | | | `stripe.TokenOptions` | {} | Data passed to stripe.createToken. (optional) | | ||
#### Methods | ||
| Method | Type | Description | | ||
|---------|------------|--------------------------| | ||
| `blur` | `(): void` | Blur the element. | | ||
| `focus` | `(): void` | Focus the element. | | ||
| `reset` | `(): void` | Reset the stripe element | | ||
#### Events | ||
| Event | Description | | ||
|-------------------------|--------------------------------------------------| | ||
| `cancel` | When a payment request is cancelled | | ||
| `error` | The validation error, or the error returned from stripe.com | | ||
| `fail` | When a payment request fails | | ||
| `payment-method` | The PaymentMethod received from stripe.com | | ||
| `ready` | Stripe has been initialized and mounted | | ||
| `shippingaddresschange` | When the user chooses a different shipping address | | ||
| `shippingoptionchange` | When the user chooses a different shipping option | | ||
| `source` | The Source received from stripe.com | | ||
| `stripe-error` | **DEPRECATED**. Will be removed in a future major version. Use `error` instead | | ||
| `stripe-payment-method` | **DEPRECATED**. Will be removed in a future major version. Use `payment-method` instead | | ||
| `stripe-ready` | **DEPRECATED**. Will be removed in a future major version. Use `ready` instead | | ||
| `stripe-source` | **DEPRECATED**. Will be removed in a future major version. Use `source` instead | | ||
| `stripe-token` | **DEPRECATED**. Will be removed in a future major version. Use `token` instead | | ||
| `success` | When a payment succeeds | | ||
| `token` | The Token received from stripe.com | | ||
#### CSS Shadow Parts | ||
| Part | Description | | ||
|----------|----------------------------------| | ||
| `error` | container for the error message | | ||
| `stripe` | container for the stripe element | | ||
#### CSS Custom Properties | ||
| Property | Description | | ||
|-----------------------------------------------|--------------------------------------------------| | ||
| `--stripe-payment-request-element-background` | background property of the container element. Default `white` | | ||
| `--stripe-payment-request-element-min-width` | min-width property of the container element. Default `300px` | | ||
| `--stripe-payment-request-element-padding` | padding property of the container element. Default `8px 12px` | | ||
@@ -17,2 +17,3 @@ import commonjs from '@rollup/plugin-commonjs'; | ||
'src/stripe-elements.js', | ||
'src/stripe-payment-request.js', | ||
], | ||
@@ -19,0 +20,0 @@ external, |
@@ -21,1 +21,12 @@ import { curry } from '@typed/curry'; | ||
}); | ||
const mapPropEntry = mapping => ([key, value]) => | ||
key in mapping && | ||
typeof mapping[key] === 'function' ? | ||
[key, mapping[key](value)] | ||
: [key, value]; | ||
export const mapProps = mapping => obj => | ||
Object.fromEntries(Object.entries(obj).map(mapPropEntry(mapping))); | ||
export const mapDataset = f => ({ dataset }) => f(dataset); |
@@ -16,3 +16,3 @@ import eagerDash from '@lavadrop/kebab-case'; | ||
* Elements forms to be embedded on a single page. | ||
* @return {String} mount element id | ||
* @return {string} mount element id | ||
*/ | ||
@@ -19,0 +19,0 @@ export function generateRandomMountElementId() { |
# Stripe Elements Web Components | ||
Custom element wrappers for Stripe.js v3 Elements. | ||
🛡⚛️🔰 **Any** Framework - **One** Stripe Integration. 💰💵💸 | ||
[](https://www.npmjs.com/package/@power-elements/stripe-elements) | ||
[](https://www.webcomponents.org/element/bennypowers/stripe-elements) | ||
[](https://open-wc.org) | ||
[](https://www.webcomponents.org/element/bennypowers/stripe-elements) | ||
[](https://www.npmjs.com/package/@power-elements/stripe-elements) | ||
[](https://codeclimate.com/github/bennypowers/stripe-elements/maintainability) | ||
@@ -9,0 +9,0 @@ [](https://codeclimate.com/github/bennypowers/stripe-elements/test_coverage) |
@@ -7,23 +7,19 @@ import '../stripe-elements.js'; | ||
import { | ||
CARD_DECLINED_ERROR, | ||
INCOMPLETE_CARD_ERROR, | ||
PUBLISHABLE_KEY, | ||
SHOULD_ERROR_KEY, | ||
SUCCESS_RESPONSES, | ||
} from '../test/mock-stripe'; | ||
import { | ||
DEFAULT_PROPS, | ||
BASE_DEFAULT_PROPS, | ||
BASE_NOTIFYING_PROPS, | ||
BASE_READ_ONLY_PROPS, | ||
EMPTY_CC_ERROR, | ||
NOTIFYING_PROPS, | ||
NO_STRIPE_CREATE_PAYMENT_METHOD_ERROR, | ||
NO_STRIPE_CREATE_SOURCE_ERROR, | ||
NO_STRIPE_CREATE_TOKEN_ERROR, | ||
NO_STRIPE_JS, | ||
READ_ONLY_PROPS, | ||
NO_STRIPE_JS_ERROR, | ||
appendAllBlueStyleTag, | ||
appendGlobalStyles, | ||
assertFiresStripeChange, | ||
assertHasOneGlobalStyleTag, | ||
assertElementErrorMessage, | ||
assertEventDetail, | ||
assertFired, | ||
assertProps, | ||
assignedNodes, | ||
awaitEvent, | ||
blur, | ||
blurStripeElement, | ||
createPaymentMethod, | ||
@@ -35,4 +31,7 @@ createSource, | ||
fetchStub, | ||
focus, | ||
focusStripeElement, | ||
initialStripe, | ||
initialStripeMountId, | ||
listenFor, | ||
mockShadyCSS, | ||
@@ -47,3 +46,2 @@ mockShadyDOM, | ||
resetTestState, | ||
restoreAppended, | ||
restoreCardClear, | ||
@@ -56,2 +54,4 @@ restoreConsoleWarn, | ||
restoreStripe, | ||
restoreStripeElementBlur, | ||
restoreStripeElementFocus, | ||
setProps, | ||
@@ -62,5 +62,7 @@ setupNoProps, | ||
spyGetComputedStyleValue, | ||
spyStripeElementBlur, | ||
spyStripeElementFocus, | ||
stubFetch, | ||
submit, | ||
synthStripeEvent, | ||
synthCardEvent, | ||
synthStripeFormValues, | ||
@@ -71,6 +73,56 @@ testDefaultPropEntry, | ||
testWritableNotifyingProp, | ||
updateComplete, | ||
validate, | ||
} from '../test/test-helpers'; | ||
import { | ||
CARD_DECLINED_ERROR, | ||
INCOMPLETE_CARD_ERROR, | ||
PUBLISHABLE_KEY, | ||
SHOULD_ERROR_KEY, | ||
SUCCESS_RESPONSES, | ||
} from '../test/mock-stripe'; | ||
import { elem, not } from './lib/predicates.js'; | ||
const DEFAULT_PROPS = Object.freeze({ | ||
...BASE_DEFAULT_PROPS, | ||
brand: null, | ||
card: null, | ||
hideIcon: false, | ||
hidePostalCode: false, | ||
iconStyle: 'default', | ||
isComplete: false, | ||
isEmpty: true, | ||
stripe: null, | ||
token: null, | ||
value: {}, | ||
}); | ||
const READ_ONLY_PROPS = Object.freeze([ | ||
...BASE_READ_ONLY_PROPS, | ||
'brand', | ||
'card', | ||
'complete', | ||
'empty', | ||
'error', | ||
'invalid', | ||
'isComplete', | ||
'isEmpty', | ||
'ready', | ||
'stripeReady', | ||
]); | ||
const NOTIFYING_PROPS = Object.freeze([ | ||
...BASE_NOTIFYING_PROPS, | ||
'brand', | ||
'card', | ||
'complete', | ||
'empty', | ||
'error', | ||
'invalid', | ||
'isComplete', | ||
'isEmpty', | ||
'ready', | ||
'stripeReady', | ||
]); | ||
describe('<stripe-elements>', function() { | ||
@@ -81,39 +133,29 @@ beforeEach(spyConsoleWarn); | ||
it('instantiates without error', async function noInitialError() { | ||
await setupNoProps(); | ||
expect(element.constructor.is).to.equal('stripe-elements'); | ||
}); | ||
describe('uses default for property', function defaults() { | ||
describe('simply instantiating', function() { | ||
beforeEach(setupNoProps); | ||
Object.entries(DEFAULT_PROPS).forEach(testDefaultPropEntry); | ||
}); | ||
it('does not throw error', function noInitialError() { | ||
expect(element.tagName).to.equal('STRIPE-ELEMENTS'); | ||
}); | ||
describe('has read-only property', function readOnly() { | ||
beforeEach(setupNoProps); | ||
READ_ONLY_PROPS.forEach(testReadOnlyProp); | ||
}); | ||
describe('uses default for property', function defaults() { | ||
beforeEach(setupNoProps); | ||
Object.entries(DEFAULT_PROPS).forEach(testDefaultPropEntry); | ||
}); | ||
describe('notifies when setting property', function notifying() { | ||
beforeEach(setupNoProps); | ||
NOTIFYING_PROPS.filter(not(elem(READ_ONLY_PROPS))).forEach(testWritableNotifyingProp); | ||
}); | ||
describe('has read-only property', function readOnly() { | ||
beforeEach(setupNoProps); | ||
READ_ONLY_PROPS.forEach(testReadOnlyProp); | ||
}); | ||
describe('notifies when privately setting read-only property', function notifying() { | ||
beforeEach(setupNoProps); | ||
READ_ONLY_PROPS.filter(elem(NOTIFYING_PROPS)).forEach(testReadonlyNotifyingProp); | ||
}); | ||
describe('notifies when setting property', function notifying() { | ||
beforeEach(setupNoProps); | ||
NOTIFYING_PROPS.filter(not(elem(READ_ONLY_PROPS))).forEach(testWritableNotifyingProp); | ||
}); | ||
describe('with global CSS present in the document', function() { | ||
beforeEach(appendGlobalStyles); | ||
beforeEach(setupNoProps); | ||
afterEach(restoreAppended); | ||
it('does not append a second stylesheet to the document', assertHasOneGlobalStyleTag); | ||
describe('notifies when privately setting read-only property', function notifying() { | ||
beforeEach(setupNoProps); | ||
READ_ONLY_PROPS.filter(elem(NOTIFYING_PROPS)).forEach(testReadonlyNotifyingProp); | ||
}); | ||
}); | ||
describe('without global CSS in the document', function() { | ||
beforeEach(setupNoProps); | ||
it('appends a stylesheet to the document', assertHasOneGlobalStyleTag); | ||
}); | ||
describe('when Mocked ShadyDOM polyfill is in use', function shadyDOM() { | ||
@@ -139,3 +181,3 @@ beforeEach(mockShadyDOM); | ||
beforeEach(async function setupOneRoot() { | ||
primaryHost = await fixture(`<primary-host></primary-host>`); | ||
primaryHost = await fixture(`<primary-host tag="stripe-elements"></primary-host>`); | ||
({ nestedElement } = primaryHost); | ||
@@ -151,3 +193,3 @@ ({ stripeMountId } = nestedElement); | ||
it('slots mount point in to its light DOM', function() { | ||
expect(primaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId })); | ||
expect(primaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId, tagName: nestedElement.constructor.is })); | ||
}); | ||
@@ -167,3 +209,3 @@ | ||
beforeEach(async function setupTwoRoots() { | ||
secondaryHost = await fixture(`<secondary-host></secondary-host>`); | ||
secondaryHost = await fixture(`<secondary-host tag="stripe-elements"></secondary-host>`); | ||
({ primaryHost } = secondaryHost); | ||
@@ -183,3 +225,3 @@ ({ nestedElement } = primaryHost); | ||
it('slots mount point in to the light DOM of the secondary shadow host', function() { | ||
expect(secondaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId })); | ||
expect(secondaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId, tagName: nestedElement.constructor.is })); | ||
}); | ||
@@ -189,3 +231,3 @@ | ||
expect(secondaryHost).shadowDom.to.equal(` | ||
<primary-host> | ||
<primary-host tag="stripe-elements"> | ||
<slot name="stripe-card" slot="stripe-card"></slot> | ||
@@ -205,3 +247,3 @@ </primary-host> | ||
beforeEach(async function setupThreeRoots() { | ||
tertiaryHost = await fixture(`<tertiary-host></tertiary-host>`); | ||
tertiaryHost = await fixture(`<tertiary-host tag="stripe-elements"></tertiary-host>`); | ||
({ secondaryHost } = tertiaryHost); | ||
@@ -223,3 +265,3 @@ ({ primaryHost } = secondaryHost); | ||
it('slots mount point in to the light DOM of the tertiary shadow host', function() { | ||
expect(tertiaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId })); | ||
expect(tertiaryHost).lightDom.to.equal(expectedLightDOM({ stripeMountId, tagName: nestedElement.constructor.is })); | ||
}); | ||
@@ -229,3 +271,3 @@ | ||
expect(tertiaryHost).shadowDom.to.equal(` | ||
<secondary-host> | ||
<secondary-host tag="stripe-elements"> | ||
<slot name="stripe-card" slot="stripe-card"></slot> | ||
@@ -238,3 +280,3 @@ </secondary-host> | ||
expect(secondaryHost).shadowDom.to.equal(` | ||
<primary-host> | ||
<primary-host tag="stripe-elements"> | ||
<slot name="stripe-card" slot="stripe-card"></slot> | ||
@@ -259,3 +301,3 @@ </primary-host> | ||
it('logs a warning', async function logsWarning() { | ||
expect(console.warn).to.have.been.calledWith(NO_STRIPE_JS); | ||
expect(console.warn).to.have.been.calledWith(`<${element.constructor.is}>: ${NO_STRIPE_JS_ERROR}`); | ||
}); | ||
@@ -267,9 +309,7 @@ | ||
it('does not mount card', async function noCard() { | ||
expect(element.card).to.not.be.ok; | ||
it('does not mount element', async function noElement() { | ||
expect(element.element).to.not.be.ok; | ||
}); | ||
it('sets the `error` property', async function setsError() { | ||
expect(element.error.message).to.equal(NO_STRIPE_JS); | ||
}); | ||
it('sets the `error` property', assertElementErrorMessage(NO_STRIPE_JS_ERROR)); | ||
@@ -281,3 +321,3 @@ it('throws an error when creating payment method', async function() { | ||
} catch (err) { | ||
expect(err.message).to.equal(NO_STRIPE_CREATE_PAYMENT_METHOD_ERROR); | ||
expect(err.message).to.equal(`<${element.constructor.is}>: ${NO_STRIPE_CREATE_PAYMENT_METHOD_ERROR}`); | ||
} | ||
@@ -291,3 +331,3 @@ }); | ||
} catch (err) { | ||
expect(err.message).to.equal(NO_STRIPE_CREATE_TOKEN_ERROR); | ||
expect(err.message).to.equal(`<${element.constructor.is}>: ${NO_STRIPE_CREATE_TOKEN_ERROR}`); | ||
} | ||
@@ -301,3 +341,3 @@ }); | ||
} catch (err) { | ||
expect(err.message).to.equal(NO_STRIPE_CREATE_SOURCE_ERROR); | ||
expect(err.message).to.equal(`<${element.constructor.is}>: ${NO_STRIPE_CREATE_SOURCE_ERROR}`); | ||
} | ||
@@ -311,3 +351,3 @@ }); | ||
} catch (err) { | ||
expect(err.message).to.equal(NO_STRIPE_CREATE_SOURCE_ERROR); | ||
expect(err.message).to.equal(`<${element.constructor.is}>: ${NO_STRIPE_CREATE_SOURCE_ERROR}`); | ||
} | ||
@@ -381,5 +421,5 @@ }); | ||
beforeEach(createPaymentMethod); | ||
it('unsets the `paymentMethod` property', assertProps({ paymentMethod: null })); | ||
it('sets the `error` property', function() { | ||
expect(element.error.message, 'error').to.equal(SHOULD_ERROR_KEY); | ||
expect(element.paymentMethod, 'paymentMethod').to.be.null; | ||
}); | ||
@@ -390,5 +430,5 @@ }); | ||
beforeEach(createSource); | ||
it('unsets the `source` property', assertProps({ source: null })); | ||
it('sets the `error` property', function() { | ||
expect(element.error.message, 'error').to.equal(SHOULD_ERROR_KEY); | ||
expect(element.source, 'source').to.be.null; | ||
}); | ||
@@ -399,5 +439,5 @@ }); | ||
beforeEach(createToken); | ||
it('unsets the `token` property', assertProps({ token: null })); | ||
it('sets the `error` property', function() { | ||
expect(element.error.message, 'error').to.equal(SHOULD_ERROR_KEY); | ||
expect(element.token, 'token').to.be.null; | ||
}); | ||
@@ -444,2 +484,34 @@ }); | ||
describe('calling blur()', function() { | ||
beforeEach(spyStripeElementBlur); | ||
beforeEach(blur); | ||
afterEach(restoreStripeElementBlur); | ||
it('calls StripeElement#blur', function() { | ||
expect(element.element.blur).to.have.been.called; | ||
}); | ||
}); | ||
describe('calling focus()', function() { | ||
beforeEach(spyStripeElementFocus); | ||
beforeEach(focus); | ||
afterEach(restoreStripeElementFocus); | ||
it('calls StripeElement#focus', function() { | ||
expect(element.element.focus).to.have.been.called; | ||
}); | ||
}); | ||
describe('when stripe element is focused', function() { | ||
beforeEach(focusStripeElement); | ||
beforeEach(updateComplete); | ||
it('sets the `focused` property', assertProps({ focused: true })); | ||
}); | ||
describe('when stripe element is blurred', function() { | ||
beforeEach(focusStripeElement); | ||
beforeEach(updateComplete); | ||
beforeEach(blurStripeElement); | ||
beforeEach(updateComplete); | ||
it('unsets the `focused` property', assertProps({ focused: false })); | ||
}); | ||
describe('when publishable key is changed', function publishableKeyReset() { | ||
@@ -479,5 +551,3 @@ let initialStripeMountId; | ||
it('sets the `error` property', function callingValidateWithoutCard() { | ||
expect(element.error.message).to.equal(EMPTY_CC_ERROR); | ||
}); | ||
it('sets the `error` property', assertElementErrorMessage(EMPTY_CC_ERROR)); | ||
}); | ||
@@ -523,62 +593,41 @@ | ||
describe('when stripe fires `ready` event', function cardReady() { | ||
beforeEach(synthStripeEvent('ready')); | ||
it('fires `stripe-ready` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-ready'); | ||
expect(ev).to.be.ok; | ||
beforeEach(listenFor('ready')); | ||
beforeEach(listenFor('stripe-ready-changed')); | ||
beforeEach(synthCardEvent('ready')); | ||
it('fires `ready` event', assertFired('ready')); | ||
it('fires `stripe-ready-changed` event', assertEventDetail('stripe-ready-changed', { value: true })); | ||
describe('after `ready` event', function() { | ||
beforeEach(awaitEvent('ready')); | ||
it('sets `stripeReady` property', assertProps({ stripeReady: true })); | ||
}); | ||
it('fires `stripe-ready-changed` event', async function() { | ||
const { detail: { value } } = await oneEvent(element, 'stripe-ready-changed'); | ||
expect(value).to.be.true; | ||
}); | ||
it('sets `stripeReady` property', async function() { | ||
await oneEvent(element, 'stripe-ready'); | ||
expect(element.stripeReady).to.be.true; | ||
}); | ||
}); | ||
describe('when stripe fires `change` event', function cardChange() { | ||
describe('without regard to the event content', function() { | ||
beforeEach(synthStripeEvent('change')); | ||
it('fires a `stripe-change` event', assertFiresStripeChange); | ||
}); | ||
beforeEach(listenFor('change')); | ||
beforeEach(synthCardEvent('change')); | ||
describe('with a `brand` property', function brandChange() { | ||
const brand = 'visa'; | ||
beforeEach(synthStripeEvent('change', { brand })); | ||
it('fires a `brand-changed` event', async function brandChanged() { | ||
const { detail: { value } } = await oneEvent(element, 'brand-changed'); | ||
expect(value).to.equal(brand); | ||
beforeEach(listenFor('brand-changed')); | ||
beforeEach(synthCardEvent('change', { brand })); | ||
it('fires a `stripe-change` event', assertFired('change')); | ||
it('fires a `brand-changed` event', assertEventDetail('brand-changed', { value: brand })); | ||
describe('and then', function() { | ||
beforeEach(awaitEvent('brand-changed')); | ||
it('sets the `brand` property', assertProps({ brand })); | ||
}); | ||
it('sets brand', async function setsBrand() { | ||
await oneEvent(element, 'brand-changed'); | ||
expect(element.brand).to.equal(brand); | ||
}); | ||
}); | ||
describe('describing a non-empty, incomplete card', function() { | ||
beforeEach(synthStripeEvent('change', { brand: 'visa', empty: false, complete: false })); | ||
it('fires `is-empty-changed` event', async function isEmptyChanged() { | ||
const { detail: { value: isEmpty } } = await oneEvent(element, 'is-empty-changed'); | ||
expect(isEmpty).to.be.false; | ||
}); | ||
describe('then after the event', function() { | ||
beforeEach(async function() { | ||
await oneEvent(element, 'stripe-change'); | ||
}); | ||
beforeEach(listenFor('is-empty-changed')); | ||
beforeEach(listenFor('empty-changed')); | ||
beforeEach(synthCardEvent('change', { brand: 'visa', empty: false, complete: false })); | ||
it('fires `is-empty-changed` event', assertEventDetail('is-empty-changed', { value: false })); | ||
describe('and then', function() { | ||
beforeEach(awaitEvent('is-empty-changed')); | ||
describe('calling reset()', function() { | ||
beforeEach(reset); | ||
afterEach(restoreCardClear); | ||
it('unsets the `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('clears the card', function() { | ||
expect(element.card.clear).to.have.been.called; | ||
expect(element.element.clear).to.have.been.called; | ||
}); | ||
@@ -590,7 +639,5 @@ }); | ||
describe('describing a complete card', function() { | ||
beforeEach(synthStripeEvent('change', { brand: 'visa', empty: false, complete: true })); | ||
it('fires `is-complete-changed` event', async function() { | ||
const { detail: { value: isComplete } } = await oneEvent(element, 'is-complete-changed'); | ||
expect(isComplete).to.be.true; | ||
}); | ||
beforeEach(listenFor('is-complete-changed')); | ||
beforeEach(synthCardEvent('change', { brand: 'visa', empty: false, complete: true })); | ||
it('fires `is-complete-changed` event', assertEventDetail('is-complete-changed', { value: true })); | ||
}); | ||
@@ -614,2 +661,3 @@ }); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('returns false', function() { | ||
@@ -620,2 +668,3 @@ expect(element.validate()).to.be.false; | ||
describe('calling isPotentiallyValid()', function() { | ||
@@ -702,23 +751,13 @@ it('returns false', function() { | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('payment-method')); | ||
beforeEach(listenFor('payment-method-changed')); | ||
beforeEach(createPaymentMethod); | ||
it('fires a `payment-method-changed` event', async function() { | ||
const ev = await oneEvent(element, 'payment-method-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.paymentMethod); | ||
}); | ||
it('fires a `stripe-payment-method` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-payment-method'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.paymentMethod); | ||
}); | ||
it('fires a `payment-method` event', assertEventDetail('payment-method', SUCCESS_RESPONSES.paymentMethod)); | ||
it('fires a `payment-method-changed` event', assertEventDetail('payment-method-changed', { value: SUCCESS_RESPONSES.paymentMethod })); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
}); | ||
@@ -741,22 +780,14 @@ | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('source')); | ||
beforeEach(listenFor('source-changed')); | ||
beforeEach(createSource); | ||
it('fires a `source-changed` event', async function() { | ||
const ev = await oneEvent(element, 'source-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
it('fires a `source` event', assertEventDetail('source', SUCCESS_RESPONSES.source)); | ||
it('fires a `source-changed` event', assertEventDetail('source-changed', { value: SUCCESS_RESPONSES.source })); | ||
it('fires a `stripe-source` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-source'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
}); | ||
@@ -779,23 +810,14 @@ | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('token')); | ||
beforeEach(listenFor('token-changed')); | ||
beforeEach(createToken); | ||
it('fires a `token` event', assertEventDetail('token', SUCCESS_RESPONSES.token)); | ||
it('fires a `token-changed` event', assertEventDetail('token-changed', { value: SUCCESS_RESPONSES.token })); | ||
it('fires a `token-changed` event', async function() { | ||
const ev = await oneEvent(element, 'token-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.token); | ||
}); | ||
it('fires a `stripe-token` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-token'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.token); | ||
}); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
}); | ||
@@ -812,28 +834,19 @@ | ||
describe('and generate unset', function() { | ||
it('calling submit() resolves with the source', function() { | ||
return element.submit() | ||
.then(result => expect(result.source).to.equal(SUCCESS_RESPONSES.source)); | ||
}); | ||
describe('calling submit()', function() { | ||
it('resolves with the source', function() { | ||
return element.submit() | ||
.then(result => expect(result.source).to.equal(SUCCESS_RESPONSES.source)); | ||
}); | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('source')); | ||
beforeEach(listenFor('source-changed')); | ||
beforeEach(submit); | ||
it('fires a `source-changed` event', async function() { | ||
const ev = await oneEvent(element, 'source-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
it('fires a `source` event', assertEventDetail('source', SUCCESS_RESPONSES.source)); | ||
it('fires a `source-changed` event', assertEventDetail('source-changed', { value: SUCCESS_RESPONSES.source })); | ||
it('fires a `stripe-source` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-source'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { expect(element.validate()).to.be.true; }); | ||
}); | ||
@@ -859,22 +872,12 @@ | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('source')); | ||
beforeEach(listenFor('source-changed')); | ||
beforeEach(submit); | ||
it('fires a `source-changed` event', async function() { | ||
const ev = await oneEvent(element, 'source-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
it('fires a `source` event', assertEventDetail('source', SUCCESS_RESPONSES.source)); | ||
it('fires a `source-changed` event', assertEventDetail('source-changed', { value: SUCCESS_RESPONSES.source })); | ||
it('fires a `stripe-source` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-source'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.source); | ||
}); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { expect(element.validate()).to.be.true; }); | ||
}); | ||
@@ -900,22 +903,12 @@ | ||
describe('subsequently', function() { | ||
beforeEach(listenFor('token')); | ||
beforeEach(listenFor('token-changed')); | ||
beforeEach(submit); | ||
it('fires a `token-changed` event', async function() { | ||
const ev = await oneEvent(element, 'token-changed'); | ||
expect(ev.detail.value).to.equal(SUCCESS_RESPONSES.token); | ||
}); | ||
it('fires a `token` event', assertEventDetail('token', SUCCESS_RESPONSES.token)); | ||
it('fires a `token-changed` event', assertEventDetail('token-changed', { value: SUCCESS_RESPONSES.token })); | ||
it('fires a `stripe-token` event', async function() { | ||
const ev = await oneEvent(element, 'stripe-token'); | ||
expect(ev.detail).to.equal(SUCCESS_RESPONSES.token); | ||
}); | ||
describe('calling validate()', function() { | ||
beforeEach(validate); | ||
it('returns true', function() { | ||
expect(element.validate()).to.be.true; | ||
}); | ||
it('does not set `error`', function() { | ||
expect(element.error).to.be.null; | ||
}); | ||
it('unsets the `error` property', assertProps({ error: null })); | ||
it('returns true', function() { expect(element.validate()).to.be.true; }); | ||
}); | ||
@@ -989,5 +982,3 @@ | ||
it('sets the `error` property', function() { | ||
expect(element.error.message).to.equal('<stripe-elements>: cannot generate something-silly'); | ||
}); | ||
it('sets the `error` property', assertElementErrorMessage('cannot generate something-silly')); | ||
@@ -994,0 +985,0 @@ describe('calling validate()', function() { |
@@ -1,29 +0,48 @@ | ||
import { LitElement, property } from 'lit-element'; | ||
import { LitElement, property, html } from 'lit-element'; | ||
import { LitNotify } from '@morbidick/lit-element-notify'; | ||
import { ifDefined } from 'lit-html/directives/if-defined'; | ||
import bound from 'bound-decorator'; | ||
import { ReadOnlyPropertiesMixin } from './lib/read-only-properties'; | ||
import { dash } from './lib/strings'; | ||
import { appendTemplate, remove } from './lib/dom'; | ||
import { dash, generateRandomMountElementId } from './lib/strings'; | ||
import { isRepresentation } from './lib/predicates'; | ||
import { stripeMethod } from './stripe-method-decorator'; | ||
import { throwBadResponse } from './lib/fetch'; | ||
/** @typedef {stripe.PaymentIntentResponse|stripe.PaymentMethodResponse|stripe.SetupIntentResponse|stripe.TokenResponse|stripe.SourceResponse} PaymentResponse */ | ||
/** @typedef {{ owner: stripe.OwnerData }} SourceData */ | ||
class StripeElementsError extends Error { | ||
constructor(tag, message) { | ||
super(`<${tag}>: ${message}`); | ||
this.originalMessage = message; | ||
} | ||
} | ||
/* istanbul ignore next */ | ||
const removeAllMounts = host => | ||
host.querySelectorAll('[slot="stripe-card"][name="stripe-card"]') | ||
.forEach(remove); | ||
const slotTemplate = | ||
html`<slot slot="stripe-card" name="stripe-card"></slot>`; | ||
const mountPointTemplate = ({ stripeMountId, tagName }) => | ||
html`<div id="${ifDefined(stripeMountId)}" class="${tagName.toLowerCase()}-mount"></div>`; | ||
/** | ||
* @fires 'stripe-error' - The validation error, or the error returned from stripe.com | ||
* @fires 'stripe-payment-intent' - The PaymentIntent received from stripe.com | ||
* @fires 'stripe-payment-method' - The PaymentMethod received from stripe.com | ||
* @fires 'stripe-source' - The Source received from stripe.com | ||
* @fires 'stripe-token' - The Token received from stripe.com | ||
* @fires 'error' - The validation error, or the error returned from stripe.com | ||
* @fires 'payment-method' - The PaymentMethod received from stripe.com | ||
* @fires 'source' - The Source received from stripe.com | ||
* @fires 'token' - The Token received from stripe.com | ||
* @fires 'success' - When a payment succeeds | ||
* @fires 'ready' - Stripe has been initialized and mounted | ||
* | ||
* @fires 'error-changed' - The new value of error | ||
* @fires 'has-error-changed' - The new value of has-error | ||
* @fires 'payment-intent-changed' - The new value of payment-intent | ||
* @fires 'payment-method-changed' - The new value of payment-method | ||
* @fires 'publishable-key-changed' - The new value of publishable-key | ||
* @fires 'source-changed' - The new value of source | ||
* @fires 'token-changed' - The new value of token | ||
* @fires 'stripe-ready' - **DEPRECATED**. Will be removed in a future major version. Use `ready` instead | ||
* @fires 'stripe-error' - **DEPRECATED**. Will be removed in a future major version. Use `error` instead | ||
* @fires 'stripe-payment-method' - **DEPRECATED**. Will be removed in a future major version. Use `payment-method` instead | ||
* @fires 'stripe-source' - **DEPRECATED**. Will be removed in a future major version. Use `source` instead | ||
* @fires 'stripe-token' - **DEPRECATED**. Will be removed in a future major version. Use `token` instead | ||
* | ||
* @csspart 'error' - container for the error message | ||
* @csspart 'stripe' - container for the stripe element | ||
*/ | ||
@@ -61,3 +80,3 @@ export class StripeBase extends ReadOnlyPropertiesMixin(LitNotify(LitElement)) { | ||
* Stripe PaymentMethod | ||
* @type {stripe.PaymentMethod} | ||
* @type {stripe.paymentMethod.PaymentMethod} | ||
* @readonly | ||
@@ -109,2 +128,8 @@ */ | ||
/** | ||
* The `client_secret` part of a Stripe `PaymentIntent` | ||
* @type {String} | ||
*/ | ||
@property({ type: String, attribute: 'client-secret' }) clientSecret; | ||
/** | ||
* Type of payment representation to generate. | ||
@@ -118,3 +143,3 @@ * @type {'payment-method'|'source'|'token'} | ||
* Stripe Publishable Key. EG. `pk_test_XXXXXXXXXXXXXXXXXXXXXXXX` | ||
* @type {String} | ||
* @type {string} | ||
*/ | ||
@@ -152,8 +177,35 @@ @property({ | ||
*/ | ||
@property({ type: String, notify: true, readOnly: true }) error = null; | ||
@property({ type: Object, notify: true, readOnly: true, reflect: true, converter: { | ||
toAttribute: error => !error ? null : error.originalMessage || error.message || '', | ||
} }) error = null; | ||
/** | ||
* If the element is focused. | ||
* @type {boolean} | ||
* @readonly | ||
*/ | ||
@property({ type: Boolean, reflect: true, notify: true, readOnly: true }) focused = false; | ||
/** | ||
* Whether the stripe element is ready to receive focus. | ||
* @type {boolean} | ||
* @readonly | ||
*/ | ||
@property({ type: Boolean, reflect: true, notify: true, readOnly: true }) ready = false; | ||
/** | ||
* Stripe instance | ||
* @type {stripe.Stripe} | ||
* @readonly | ||
*/ | ||
@property({ type: Object, readOnly: true }) stripe = null; | ||
// DEPRECATED | ||
/** | ||
* Whether the element has an error | ||
* @type {Boolean} | ||
* **DEPRECATED**. Will be removed in a future version. Use `error` instead | ||
* @type {boolean} | ||
* @readonly | ||
* @deprecated | ||
*/ | ||
@@ -169,7 +221,14 @@ @property({ | ||
/** | ||
* Stripe instance | ||
* @type {stripe.Stripe} | ||
* @readonly | ||
* Whether the stripe element is ready to receive focus. | ||
* **DEPRECATED**. Will be removed in a future version. use `ready` instead. | ||
* @deprecated | ||
* @type {boolean} | ||
*/ | ||
@property({ type: Object, readOnly: true }) stripe = null; | ||
@property({ | ||
type: Boolean, | ||
attribute: 'stripe-ready', | ||
reflect: true, | ||
notify: true, | ||
readOnly: true, | ||
}) stripeReady = false; | ||
@@ -179,12 +238,45 @@ /* PRIVATE FIELDS */ | ||
/** | ||
* The id for the stripe mount point | ||
* @type {string} | ||
* Breadcrumbs back up to the document. | ||
* @type {Node[]} | ||
* @private | ||
*/ | ||
stripeMountId; | ||
shadowHosts = []; | ||
/** | ||
* Stripe.js mount point element. Due to limitations in the Stripe.js library, this element must be connected to the document. | ||
* @type {Element} | ||
*/ | ||
get stripeMount() { return document.getElementById(this.stripeMountId); } | ||
/** | ||
* Stripe.js mount point element id. Due to limitations in the Stripe.js library, this element must be connected to the document. | ||
* @type {string} | ||
* @protected | ||
*/ | ||
stripeMountId = generateRandomMountElementId(); | ||
/* LIFECYCLE */ | ||
/** @inheritdoc */ | ||
render() { | ||
const { error, showError } = this; | ||
const errorMessage = error?.originalMessage || error?.message; | ||
return html` | ||
<div id="stripe" part="stripe" tabindex="0" @focus="${this.focus}" @blur="${this.blur}"> | ||
<slot id="stripe-slot" name="stripe-card"></slot> | ||
</div> | ||
<div id="error" part="error" ?hidden="${!showError}">${ifDefined(errorMessage)}</div> | ||
`; | ||
} | ||
/** @inheritdoc */ | ||
firstUpdated() { | ||
this.destroyMountPoints(); | ||
this.initMountPoints(); | ||
} | ||
/** @inheritdoc */ | ||
updated(changed) { | ||
super.updated?.(changed); | ||
if (changed.has('error')) this.errorChanged(); | ||
@@ -198,59 +290,36 @@ if (changed.has('publishableKey')) this.init(); | ||
/** | ||
* Submit payment information to generate a paymentMethod | ||
* @param {stripe.PaymentMethodData} [paymentMethodData={}] | ||
* @resolves {stripe.PaymentMethodResponse} | ||
* Reset the stripe element | ||
*/ | ||
@stripeMethod async createPaymentMethod(paymentMethodData = this.getPaymentMethodData()) { | ||
return this.stripe.createPaymentMethod(paymentMethodData); | ||
reset() { | ||
this.element?.clear?.(); | ||
this.resetRepresentations(); | ||
this.set({ error: null }); | ||
} | ||
/** | ||
* Submit payment information to generate a source | ||
* @param {{ owner: stripe.OwnerInfo }} [sourceData={}] | ||
* @resolves {stripe.SourceResponse} | ||
*/ | ||
@stripeMethod async createSource(sourceData = this.sourceData) { | ||
return this.stripe.createSource(this.element, sourceData); | ||
/** Blur the element. */ | ||
blur() { | ||
this.element?.blur(); | ||
} | ||
/** | ||
* Submit payment information to generate a token | ||
* @param {TokenData} [tokenData=this.tokenData] | ||
* @resolves {stripe.TokenResponse} | ||
*/ | ||
@stripeMethod async createToken(tokenData = this.tokenData) { | ||
return this.stripe.createToken(this.element, tokenData); | ||
/** Focus the element. */ | ||
focus() { | ||
this.element?.focus(); | ||
} | ||
/** | ||
* Reset the stripe element | ||
*/ | ||
reset() { | ||
this.resetRepresentations(); | ||
this.set({ error: null }); | ||
} | ||
/* PRIVATE API */ | ||
/** | ||
* Generates a payment representation of the type specified by `generate`. | ||
* @resolves {PaymentResponse} | ||
* Creates a new StripeElementsError | ||
* @param {string} message | ||
* @return {StripeElementsError} | ||
* @private | ||
*/ | ||
async submit() { | ||
switch (this.generate) { | ||
case 'payment-method': return this.createPaymentMethod(); | ||
case 'source': return this.createSource(); | ||
case 'token': return this.createToken(); | ||
default: { | ||
const error = new Error(`<${this.constructor.is}>: cannot generate ${this.generate}`); | ||
await this.set({ error }); | ||
throw error; | ||
} | ||
} | ||
createError(message) { | ||
return new StripeElementsError(this.constructor.is, message); | ||
} | ||
/* PRIVATE API */ | ||
/** @private */ | ||
async errorChanged() { | ||
const hasError = !!this.error; | ||
await this.set({ hasError }); | ||
await this.set({ hasError }); // DEPRECATED | ||
this.resetRepresentations(); | ||
@@ -262,9 +331,9 @@ this.fireError(this.error); | ||
* Fires an event. | ||
* @param {String} type event type | ||
* @param {string} type event type | ||
* @param {any} detail detail value | ||
* @param {Object} [opts={}] event options | ||
* @param {EventInit} [opts={}] | ||
* @private | ||
*/ | ||
fire(type, detail, opts = {}) { | ||
this.dispatchEvent(new CustomEvent(type, { bubbles: true, detail, ...opts })); | ||
this.dispatchEvent(new CustomEvent(type, { detail, ...opts })); | ||
} | ||
@@ -278,2 +347,4 @@ | ||
fireError(error) { | ||
this.dispatchEvent(new ErrorEvent('error', { error })); | ||
// DEPRECATED | ||
this.dispatchEvent(new ErrorEvent('stripe-error', { bubbles: true, error })); | ||
@@ -283,2 +354,14 @@ } | ||
/** | ||
* Gets a CSS Custom Property value, respecting ShadyCSS. | ||
* @param {string} propertyName CSS Custom Property | ||
* @param {CSSStyleDeclaration} [precomputedStyle] pre-computed style declaration | ||
* @return {any} | ||
* @private | ||
*/ | ||
getCSSCustomPropertyValue(propertyName, precomputedStyle) { | ||
if (window.ShadyCSS) return ShadyCSS.getComputedStyleValue(this, propertyName); | ||
else return precomputedStyle.getPropertyValue(propertyName); | ||
} | ||
/** | ||
* Sets the token or error from the response. | ||
@@ -297,2 +380,67 @@ * @param {PaymentResponse} response Stripe Response | ||
/** | ||
* Reinitializes Stripe and mounts the element. | ||
* @private | ||
*/ | ||
async init() { | ||
this.destroyMountPoints(); | ||
this.initMountPoints(); | ||
await this.unmount(); | ||
await this.initStripe(); | ||
await this.initElement(); | ||
this.initElementListeners(); | ||
this.mount(); | ||
} | ||
/** @private */ | ||
destroyMountPoints() { | ||
this.shadowHosts.forEach(removeAllMounts); | ||
if (this.stripeMount) this.stripeMount.remove(); | ||
} | ||
/** @private */ | ||
initElementListeners() { | ||
this.element.addEventListener('ready', this.onReady); | ||
this.element.addEventListener('focus', this.onFocus); | ||
this.element.addEventListener('blur', this.onBlur); | ||
} | ||
/** @private */ | ||
initMountPoints() { | ||
this.stripeMountId = generateRandomMountElementId(); | ||
if (window.ShadyDOM) appendTemplate(mountPointTemplate(this), this); | ||
else this.initShadowMountPoints(); | ||
} | ||
/** | ||
* Prepares to mount Stripe Elements in light DOM. | ||
* @private | ||
*/ | ||
initShadowMountPoints() { | ||
// trace each shadow boundary between us and the document | ||
let host = this; | ||
this.shadowHosts = [this]; | ||
while (host = host.getRootNode().host) this.shadowHosts.push(host); // eslint-disable-line prefer-destructuring, no-loops/no-loops | ||
const { shadowHosts } = this; | ||
// Prepare the shallowest breadcrumb slot at document level | ||
const hosts = [...shadowHosts]; | ||
const root = hosts.pop(); | ||
if (!root.querySelector('[slot="stripe-card"]')) { | ||
const div = document.createElement('div'); | ||
div.slot = 'stripe-card'; | ||
root.appendChild(div); | ||
} | ||
const container = root.querySelector('[slot="stripe-card"]'); | ||
// Render the form to the document, so that Stripe.js can mount | ||
appendTemplate(mountPointTemplate(this), container); | ||
// Append breadcrumb slots to each shadowroot in turn, | ||
// from the document down to the <stripe-elements> instance. | ||
hosts.forEach(appendTemplate(slotTemplate)); | ||
} | ||
/** | ||
* Initializes Stripe and elements. | ||
@@ -305,3 +453,3 @@ * @private | ||
const elements = stripe && stripe.elements(); | ||
const error = stripe ? null : new Error(`<${this.constructor.is}> requires Stripe.js to be loaded first.`); | ||
const error = stripe ? null : this.createError('requires Stripe.js to be loaded first.'); | ||
if (error) console.warn(error.message); // eslint-disable-line no-console | ||
@@ -312,41 +460,97 @@ await this.set({ elements, error, stripe }); | ||
/** | ||
* POSTs the payment info represenation to the endpoint at `/action` | ||
* Mounts the Stripe Element | ||
* @private | ||
*/ | ||
async postRepresentation() { | ||
const token = this.token || undefined; | ||
const source = this.source || undefined; | ||
const paymentMethod = this.paymentMethod || undefined; | ||
const body = JSON.stringify({ token, source, paymentMethod }); | ||
const headers = { 'Content-Type': 'application/json' }; | ||
const method = 'POST'; | ||
return fetch(this.action, { body, headers, method }) | ||
.then(throwBadResponse) | ||
.then(success => this.fire('stripe-payment-success', success)) | ||
.catch(error => this.set({ error })); | ||
mount() { | ||
/* istanbul ignore next */ | ||
if (!this.stripeMount) throw this.createError('Stripe Mount missing'); | ||
this.element?.mount(this.stripeMount); | ||
} | ||
/** | ||
* @param {String} name | ||
* Unmounts and nullifies the card. | ||
* @private | ||
*/ | ||
@bound representationChanged(name) { | ||
if (!isRepresentation(name)) return; | ||
const value = this[name]; | ||
/* istanbul ignore if */ | ||
if (!value) return; | ||
this.fire(`stripe-${dash(name)}`, value); | ||
if (this.action) this.postRepresentation(); | ||
async unmount() { | ||
this.element?.unmount(); | ||
await this.set({ element: null }); | ||
} | ||
/** | ||
* @param {StripeFocusEvent} event | ||
* @private | ||
*/ | ||
@bound async onBlur(event) { | ||
await this.set({ focused: false }); | ||
} | ||
/** | ||
* @param {StripeFocusEvent} event | ||
* @private | ||
*/ | ||
@bound async onFocus(event) { | ||
await this.set({ focused: true }); | ||
} | ||
/** | ||
* Sets the `ready` property when the stripe element is ready to receive focus. | ||
* @param {Event} event | ||
* @private | ||
*/ | ||
@bound async onReady(event) { | ||
await this.set({ ready: true, stripeReady: true }); | ||
this.fire('ready', event); | ||
// DEPRECATED | ||
this.fire('stripe-ready', event); | ||
} | ||
/** | ||
* POSTs the payment info represenation to the endpoint at `/action` | ||
* @private | ||
*/ | ||
async postRepresentation() { | ||
const token = this.token || undefined; | ||
const source = this.source || undefined; | ||
const paymentMethod = this.paymentMethod || undefined; | ||
const body = JSON.stringify({ token, source, paymentMethod }); | ||
const headers = { 'Content-Type': 'application/json' }; | ||
const method = 'POST'; | ||
return fetch(this.action, { body, headers, method }) | ||
.then(throwBadResponse) | ||
.then(success => { | ||
this.fire('success', success); | ||
// DEPRECATED | ||
this.fire('stripe-payment-success', success); | ||
}) | ||
.catch(error => this.set({ error })); | ||
} | ||
/** | ||
* @param {string} name | ||
* @private | ||
*/ | ||
@bound representationChanged(name) { | ||
if (!isRepresentation(name)) return; | ||
const value = this[name]; | ||
/* istanbul ignore if */ | ||
if (!value) return; | ||
// DEPRECATED | ||
this.fire(`stripe-${dash(name)}`, value); | ||
this.fire(`${dash(name)}`, value); | ||
if (this.action) this.postRepresentation(); | ||
} | ||
/** @private */ | ||
resetRepresentations() { | ||
this.set({ | ||
paymentIntent: null, | ||
paymentMethod: null, | ||
token: null, | ||
source: null, | ||
setupIntent: null, | ||
}); | ||
} | ||
} | ||
/** @typedef {stripe.PaymentIntentResponse|stripe.PaymentMethodResponse|stripe.SetupIntentResponse|stripe.TokenResponse|stripe.SourceResponse} PaymentResponse */ | ||
/** @typedef {{ owner: stripe.OwnerData }} SourceData */ | ||
/** @typedef {{ elementType: stripe.elements.elementsType }} StripeFocusEvent */ |
import { LitNotify } from '@morbidick/lit-element-notify'; | ||
import { html, property } from 'lit-element'; | ||
import { property } from 'lit-element'; | ||
import { ifDefined } from 'lit-html/directives/if-defined'; | ||
import bound from 'bound-decorator'; | ||
import { StripeBase } from './StripeBase'; | ||
import { appendTemplate, remove } from './lib/dom'; | ||
import { dash, generateRandomMountElementId } from './lib/strings'; | ||
import { dash } from './lib/strings'; | ||
import { stripeMethod } from './lib/stripe-method-decorator'; | ||
import sharedStyles from './shared.css'; | ||
import style from './stripe-elements.css'; | ||
/* istanbul ignore next */ | ||
const removeAllMounts = host => | ||
host.querySelectorAll('[slot="stripe-card"][name="stripe-card"]') | ||
.forEach(remove); | ||
const slotTemplate = | ||
html`<slot slot="stripe-card" name="stripe-card"></slot>`; | ||
const mountPointTemplate = ({ stripeMountId }) => | ||
html`<div id="${ifDefined(stripeMountId)}" class="stripe-elements-mount"></div>`; | ||
const stripeElementsCustomCssTemplate = document.createElement('template'); | ||
stripeElementsCustomCssTemplate.id = 'stripe-elements-custom-css-properties'; | ||
stripeElementsCustomCssTemplate.innerHTML = ` | ||
<style id="stripe-elements-custom-css-properties"> | ||
.StripeElement { | ||
background-color: white; | ||
padding: 8px 12px; | ||
border-radius: 4px; | ||
border: 1px solid transparent; | ||
box-shadow: 0 1px 3px 0 #e6ebf1; | ||
-webkit-transition: box-shadow 150ms ease; | ||
transition: box-shadow 150ms ease; | ||
min-width: var(--stripe-elements-width, 300px); | ||
padding: var(--stripe-elements-element-padding, 14px); | ||
background: var(--stripe-elements-element-background, initial); | ||
} | ||
.StripeElement--focus { | ||
box-shadow: 0 1px 3px 0 #cfd7df; | ||
} | ||
.StripeElement--invalid { | ||
border-color: #fa755a; | ||
} | ||
.StripeElement--webkit-autofill { | ||
background-color: #fefde5 !important; | ||
} | ||
</style> | ||
`; | ||
function applyCustomCss() { | ||
if (!document.getElementById('stripe-elements-custom-css-properties')) { | ||
document.head.appendChild(stripeElementsCustomCssTemplate.content.cloneNode(true)); | ||
} | ||
} | ||
const allowedStyles = [ | ||
@@ -97,3 +48,2 @@ 'color', | ||
* ```html | ||
* <script type="module" src="https://unpkg.com/@power-elements/stripe-elements/stripe-elements.js?module"></script> | ||
* <stripe-elements id="stripe" | ||
@@ -106,9 +56,8 @@ * action="/payment" | ||
* See the demos for more comprehensive examples. | ||
* - Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-plain-html-and-javascript). | ||
* - Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-lit-element). | ||
* - Using `<stripe-elements>` in a [Polymer Element](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-polymer-element). | ||
* - Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-vue-component). | ||
* - Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-an-angular-component). | ||
* - Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-react-component). | ||
* - Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/stripe-elements--in-a-preact-component). | ||
* - Using `<stripe-elements>` with [plain HTML and JavaScript](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-html--stripe-elements). | ||
* - Using `<stripe-elements>` in a [LitElement](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-litelement--stripe-elements). | ||
* - Using `<stripe-elements>` in a [Vue Component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-vue--stripe-elements). | ||
* - Using `<stripe-elements>` in an [Angular component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-angular--stripe-elements). | ||
* - Using `<stripe-elements>` in a [React component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-react--stripe-elements). | ||
* - Using `<stripe-elements>` in a [Preact component](https://bennypowers.dev/stripe-elements/?path=/docs/framework-examples-preact--stripe-elements). | ||
* | ||
@@ -143,2 +92,6 @@ * ## Styling | ||
* | ||
* @cssprop [--stripe-elements-border-radius] - border radius of the element container. Default `4px` | ||
* @cssprop [--stripe-elements-border] - border property of the element container. Default `1px solid transparent` | ||
* @cssprop [--stripe-elements-box-shadow] - box shadow for the element container. Default `0 1px 3px 0 #e6ebf1` | ||
* @cssprop [--stripe-elements-transition] - transition property for the element container. Default `box-shadow 150ms ease` | ||
* | ||
@@ -196,10 +149,3 @@ * @cssprop [--stripe-elements-base-color] - `color` property for the element in its base state | ||
* | ||
* @fires 'stripe-change' - Stripe Element change event | ||
* @fires 'stripe-ready' - Stripe has been initialized and mounted | ||
* | ||
* @fires 'brand-changed' - The new value of brand | ||
* @fires 'card-changed' - The new value of card | ||
* @fires 'is-complete-changed' - The new value of is-complete | ||
* @fires 'is-empty-changed' - The new value of is-empty | ||
* @fires 'stripe-ready-changed' - The new value of stripe-ready | ||
* @fires 'change' - Stripe Element change event | ||
*/ | ||
@@ -220,3 +166,3 @@ export class StripeElements extends LitNotify(StripeBase) { | ||
* Whether to hide icons in the Stripe form. | ||
* @type {Boolean} | ||
* @type {boolean} | ||
*/ | ||
@@ -228,3 +174,3 @@ @property({ type: Boolean, attribute: 'hide-icon' }) hideIcon = false; | ||
* Useful when you gather shipping info elsewhere. | ||
* @type {Boolean} | ||
* @type {boolean} | ||
*/ | ||
@@ -241,3 +187,3 @@ @property({ type: Boolean, attribute: 'hide-postal-code' }) hidePostalCode = false; | ||
* Prefilled values for form. Example {postalCode: '90210'} | ||
* @type {Object} | ||
* @type {object} | ||
*/ | ||
@@ -250,3 +196,3 @@ @property({ type: Object }) value = {}; | ||
* The card brand detected by Stripe | ||
* @type {String} | ||
* @type {string} | ||
* @readonly | ||
@@ -257,24 +203,39 @@ */ | ||
/** | ||
* The Stripe card object. | ||
* @type {stripe.Element} | ||
* Whether the form is complete. | ||
* @type {boolean} | ||
* @readonly | ||
*/ | ||
@property({ type: Object, notify: true, readOnly: true }) card = null; | ||
@property({ type: Boolean, reflect: true, notify: true, readOnly: true }) complete = false; | ||
/** | ||
* If the form is complete. | ||
* @type {Boolean} | ||
* If the form is empty. | ||
* @type {boolean} | ||
* @readonly | ||
*/ | ||
@property({ | ||
type: Boolean, | ||
attribute: 'is-complete', | ||
reflect: true, | ||
notify: true, | ||
readOnly: true, | ||
}) isComplete = false; | ||
@property({ type: Boolean, reflect: true, notify: true, readOnly: true }) empty = true; | ||
/** | ||
* If the form is empty. | ||
* @type {Boolean} | ||
* Whether the form is invalid. | ||
* @type {boolean} | ||
* @readonly | ||
*/ | ||
@property({ type: Boolean, reflect: true, notify: true, readOnly: true }) invalid = false; | ||
// DEPRECATED | ||
/** | ||
* The Stripe card object. | ||
* **DEPRECATED**. Will be removed in a future version. use `element` instead | ||
* @type {stripe.elements.Element} | ||
* @readonly | ||
* @deprecated | ||
*/ | ||
@property({ type: Object, notify: true, readOnly: true }) card = null; | ||
/** | ||
* Whether the form is empty. | ||
* **DEPRECATED**. Will be removed in a future version. use `empty` instead | ||
* @type {boolean} | ||
* @deprecated | ||
*/ | ||
@property({ | ||
@@ -289,66 +250,56 @@ type: Boolean, | ||
/** | ||
* If the stripe element is ready to receive focus. | ||
* @type {Boolean} | ||
* Whether the form is complete. | ||
* **DEPRECATED**. Will be removed in a future version. use `complete` instead | ||
* @type {boolean} | ||
* @deprecated | ||
*/ | ||
@property({ | ||
type: Boolean, | ||
attribute: 'stripe-ready', | ||
attribute: 'is-complete', | ||
reflect: true, | ||
notify: true, | ||
readOnly: true, | ||
}) stripeReady = false; | ||
}) isComplete = false; | ||
/* PRIVATE FIELDS */ | ||
updated(changed) { | ||
super.updated(changed); | ||
// DEPRECATED | ||
if (changed.has('element') && !this.element) this.set({ card: null }); | ||
} | ||
/* PUBLIC API */ | ||
/** | ||
* Breadcrumbs back up to the document. | ||
* @type {Node[]} | ||
* @private | ||
* Submit payment information to generate a paymentMethod | ||
* @param {stripe.PaymentMethodData} [paymentMethodData={}] | ||
* @resolves {stripe.PaymentMethodResponse} | ||
*/ | ||
shadowHosts = []; | ||
@stripeMethod async createPaymentMethod(paymentMethodData = this.getPaymentMethodData()) { | ||
return this.stripe.createPaymentMethod(paymentMethodData); | ||
} | ||
/** | ||
* Stripe Element mount point | ||
* @type {Element} | ||
* Submit payment information to generate a source | ||
* @param {{ owner: stripe.OwnerInfo }} [sourceData={}] | ||
* @resolves {stripe.SourceResponse} | ||
*/ | ||
get stripeMount() { return document.getElementById(this.stripeMountId); } | ||
@stripeMethod async createSource(sourceData = this.sourceData) { | ||
return this.stripe.createSource(this.element, sourceData); | ||
} | ||
/** | ||
* Mount Point Element id | ||
* @type {String} | ||
* @protected | ||
* Submit payment information to generate a token | ||
* @param {TokenData} [tokenData=this.tokenData] | ||
* @resolves {stripe.TokenResponse} | ||
*/ | ||
stripeMountId; | ||
/* LIFECYCLE */ | ||
/** @inheritdoc */ | ||
connectedCallback() { | ||
super.connectedCallback(); | ||
applyCustomCss(); | ||
@stripeMethod async createToken(tokenData = this.tokenData) { | ||
return this.stripe.createToken(this.element, tokenData); | ||
} | ||
/** @inheritdoc */ | ||
firstUpdated() { | ||
this.resetMount(); | ||
} | ||
/** @inheritdoc */ | ||
render() { | ||
const { error, showError } = this; | ||
const { message: errorMessage = '' } = error || {}; | ||
return html` | ||
<slot id="stripe-slot" name="stripe-card"></slot> | ||
<div id="error" part="error" ?hidden="${!showError}">${errorMessage || error}</div> | ||
`; | ||
} | ||
/* PUBLIC API */ | ||
/** | ||
* Checks for potential validity. A potentially valid form is one that is not empty, not complete and has no error. A validated form also counts as potentially valid. | ||
* @return {Boolean} true if the Stripe form is potentially valid | ||
* @return {boolean} true if the Stripe form is potentially valid | ||
*/ | ||
isPotentiallyValid() { | ||
return (!this.isComplete && !this.isEmpty && !this.hasError) || this.validate(); | ||
return (!this.complete && !this.empty && !this.error) || this.validate(); | ||
} | ||
@@ -361,14 +312,30 @@ | ||
super.reset(); | ||
this.element && this.element.clear(); | ||
this.element?.clear(); | ||
} | ||
/** | ||
* Generates a payment representation of the type specified by `generate`. | ||
* @resolves {PaymentResponse} | ||
*/ | ||
async submit() { | ||
switch (this.generate) { | ||
case 'payment-method': return this.createPaymentMethod(); | ||
case 'source': return this.createSource(); | ||
case 'token': return this.createToken(); | ||
default: { | ||
const error = this.createError(`cannot generate ${this.generate}`); | ||
await this.set({ error }); | ||
throw error; | ||
} | ||
} | ||
} | ||
/** | ||
* Checks if the Stripe form is valid. | ||
* @return {Boolean} true if the Stripe form is valid | ||
* @return {boolean} true if the Stripe form is valid | ||
*/ | ||
validate() { | ||
const { isComplete, isEmpty, hasError } = this; | ||
const isValid = !hasError && isComplete && !isEmpty; | ||
const error = new Error(`Credit card information is ${isEmpty ? 'empty' : 'incomplete'}.`); | ||
if (!isValid && !hasError) this.set({ error }); | ||
const { complete, empty, error } = this; | ||
const isValid = !error && complete && !empty; | ||
if (empty && !error) this.set({ error: this.createError('Your card number is empty.') }); | ||
return isValid; | ||
@@ -387,3 +354,3 @@ } | ||
const type = 'card'; | ||
const { billingDetails, card, paymentMethodData } = this; | ||
const { billingDetails, element: card, paymentMethodData } = this; | ||
return ({ | ||
@@ -399,111 +366,44 @@ billing_details: billingDetails, | ||
* Returns a Stripe-friendly style object computed from CSS custom properties | ||
* @return {Object} Stripe Style initialization object. | ||
* @return {StripeStyleInit} Stripe Style initialization object. | ||
* @private | ||
*/ | ||
getStripeElementsStyles() { | ||
const computedStyle = window.ShadyCSS ? null : getComputedStyle(this); | ||
return allowedStyles.reduce((acc, camelCase) => { | ||
const dashCase = dash(camelCase); | ||
Object.keys(acc).forEach(prefix => { | ||
const customProperty = `--stripe-elements-${prefix}-${dashCase}`; | ||
const propertyValue = | ||
computedStyle ? computedStyle.getPropertyValue(customProperty) | ||
: ShadyCSS.getComputedStyleValue(this, customProperty); | ||
acc[prefix][camelCase] = propertyValue || undefined; | ||
}); | ||
return acc; | ||
}, { base: {}, complete: {}, empty: {}, invalid: {} }); | ||
const computedStyle = window.ShadyCSS ? undefined : getComputedStyle(this); | ||
const getStyle = prop => this.getCSSCustomPropertyValue(prop, computedStyle) || undefined; | ||
const styleReducer = ({ base = {}, complete = {}, empty = {}, invalid = {} }, camelCase) => ({ | ||
base: { ...base, [camelCase]: getStyle(`--stripe-elements-base-${dash(camelCase)}`) }, | ||
complete: { ...complete, [camelCase]: getStyle(`--stripe-elements-complete-${dash(camelCase)}`) }, | ||
empty: { ...empty, [camelCase]: getStyle(`--stripe-elements-empty-${dash(camelCase)}`) }, | ||
invalid: { ...invalid, [camelCase]: getStyle(`--stripe-elements-invalid-${dash(camelCase)}`) }, | ||
}); | ||
return allowedStyles.reduce(styleReducer, {}); | ||
} | ||
/** | ||
* Reinitializes Stripe and mounts the card. | ||
* @private | ||
*/ | ||
async init() { | ||
this.resetMount(); | ||
await this.unmount(); | ||
await this.initStripe(); | ||
await this.mount(); | ||
} | ||
/** @private */ | ||
initMountPoint() { | ||
this.stripeMountId = generateRandomMountElementId(); | ||
if (window.ShadyDOM) this.initShadyDOMMount(); | ||
else this.initShadowDOMMounts(); | ||
} | ||
/** | ||
* Prepares to mount Stripe Elements in light DOM. | ||
* @private | ||
*/ | ||
initShadowDOMMounts() { | ||
// trace each shadow boundary between us and the document | ||
let host = this; | ||
this.shadowHosts = [this]; | ||
while (host = host.getRootNode().host) this.shadowHosts.push(host); // eslint-disable-line prefer-destructuring, no-loops/no-loops | ||
const { shadowHosts, stripeMountId } = this; | ||
// Prepare the shallowest breadcrumb slot at document level | ||
const hosts = [...shadowHosts]; | ||
const root = hosts.pop(); | ||
if (!root.querySelector('[slot="stripe-card"]')) { | ||
const div = document.createElement('div'); | ||
div.slot = 'stripe-card'; | ||
root.appendChild(div); | ||
} | ||
const container = root.querySelector('[slot="stripe-card"]'); | ||
// Render the form to the document, so that Stripe.js can mount | ||
appendTemplate(mountPointTemplate({ stripeMountId }), container); | ||
// Append breadcrumb slots to each shadowroot in turn, | ||
// from the document down to the <stripe-elements> instance. | ||
hosts.forEach(appendTemplate(slotTemplate)); | ||
} | ||
/** | ||
* Creates a mounting div for the shady dom stripe elements container | ||
* @private | ||
*/ | ||
initShadyDOMMount() { | ||
const { stripeMountId } = this; | ||
const mountTemplate = mountPointTemplate({ stripeMountId }); | ||
appendTemplate(mountTemplate, this); | ||
} | ||
/** | ||
* Creates and mounts Stripe Elements card. | ||
* @private | ||
*/ | ||
async mount() { | ||
async initElement() { | ||
if (!this.stripe) return; | ||
const { hidePostalCode, hideIcon, iconStyle, value } = this; | ||
const style = this.getStripeElementsStyles(); | ||
const options = { hideIcon, hidePostalCode, iconStyle, style, value }; | ||
const element = this.elements | ||
.create('card', { hideIcon, hidePostalCode, iconStyle, style, value }); | ||
const element = this.elements.create('card', options); | ||
await this.set({ element, card: element }); | ||
/* istanbul ignore if */ | ||
if (!this.stripeMount) throw new Error('Stripe Mount missing'); | ||
element.mount(this.stripeMount); | ||
element.addEventListener('ready', this.onReady); | ||
element.addEventListener('change', this.onChange); | ||
await this.set({ isComplete: false, isEmpty: true }); | ||
await this.set({ | ||
element, | ||
// DEPRECATED | ||
card: element, | ||
}); | ||
} | ||
/** | ||
* Sets the error. | ||
* @param {StripeChangeEvent} event | ||
* @param {Boolean} event.empty true if value is empty | ||
* @param {Boolean} event.complete true if value is well-formed and potentially complete. | ||
* @param {String} event.brand brand of the card being entered e.g. 'visa' or 'amex' | ||
* @param {Object} event.error The current validation error, if any. | ||
* Updates the element's state. | ||
* @param {stripe.elements.ElementChangeResponse} event | ||
* @param {boolean} event.empty true if value is empty | ||
* @param {boolean} event.complete true if value is well-formed and potentially complete. | ||
* @param {string} event.brand brand of the card being entered e.g. 'visa' or 'amex' | ||
* @param {stripe.Error} event.error The current validation error, if any. | ||
* @param {String|Object} event.value Value of the form. Only non-sensitive information e.g. postalCode is present. | ||
@@ -513,37 +413,21 @@ * @private | ||
@bound async onChange(event) { | ||
const { brand, complete: isComplete, empty: isEmpty, error = null } = event; | ||
await this.set({ error, brand, isComplete, isEmpty }); | ||
const { brand, complete, empty, error = null } = event; | ||
const invalid = error || (!empty && !complete); | ||
await this.set({ | ||
brand, | ||
complete, | ||
empty, | ||
error, | ||
invalid, | ||
// DEPRECATED | ||
isComplete: complete, | ||
isEmpty: empty, | ||
}); | ||
this.fire('change', event); | ||
// DEPRECATED | ||
this.fire('stripe-change', event); | ||
} | ||
} | ||
/** | ||
* Sets the stripeReady property when the stripe element is ready to receive focus. | ||
* @param {Event} event | ||
* @private | ||
*/ | ||
@bound async onReady(event) { | ||
await this.set({ stripeReady: true }); | ||
this.fire('stripe-ready', event); | ||
} | ||
/** @private */ | ||
removeStripeMounts() { | ||
this.shadowHosts.forEach(removeAllMounts); | ||
if (this.stripeMount) this.stripeMount.remove(); | ||
} | ||
/** @private */ | ||
resetMount() { | ||
this.removeStripeMounts(); | ||
this.initMountPoint(); | ||
} | ||
/** | ||
* Unmounts and nullifies the card. | ||
* @private | ||
*/ | ||
async unmount() { | ||
this.element?.unmount(); | ||
await this.set({ card: null, element: null, stripeReady: false }); | ||
} | ||
} | ||
/** @typedef {{ base?: stripe.elements.Style, complete?: stripe.elements.Style, empty?: stripe.elements.Style, invalid?: stripe.elements.Style}} StripeStyleInit */ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
410310
32
6480
366
3
Updated@open-wc/lit-helpers@^0.2.6