Skip to content

Commit

Permalink
Merge pull request #67 from square/deanpapastrat/add-gift-card-element
Browse files Browse the repository at this point in the history
Add support for gift card elements
  • Loading branch information
deanpapastrat authored Oct 27, 2020
2 parents 42e4472 + 812db07 commit fddc0f7
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 0 deletions.
73 changes: 73 additions & 0 deletions addon/components/square-payment-form-gift-card-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from '../templates/components/square-payment-form-gift-card-input';

const DEFAULT_PLACEHOLDER = '• • • • • • • • • • • • • • • •';

/**
* Renders a placeholder element that gets replaced with a secure Square `<iframe>` tag by the SqPaymentForm JS
* library. The `<iframe>` then renders an `<input>` tag for the gift card number.
*
* When accepting gift cards, you **must** have this component inside your form AND
* not include any other components.
*
* This component may only be used in *yielded* form, where it is yielded by the
* `SquarePaymentForm` component as the `GiftCardInput` property.
*
* **Example: Render a gift card input inside the payment form**
* ```hbs
* {{#square-payment-form as |PaymentForm|}}
* {{PaymentForm.GiftCardInput}}
* {{/square-payment-form}}
* ```
*
* @class SquarePaymentFormGiftCardInput
*/
export default Component.extend({
layout,

/**
* Greyed-out string that shows up in the gift card input field before a customer begins
* typing in their gift card number.
*
* **Default Value:** `• • • • • • • • • • • • • • • •`
*
* **Example: Replace the Default Placeholder**
*
* ```hbs
* {{#square-payment-form as |PaymentForm|}}
* {{PaymentForm.GiftCardInput placeholder="1234 5678 9012 3456"}}
* {{/square-payment-form}}
* ```
*
* @argument placeholder
* @type String
*/
placeholder: null,

/**
* Passed down unique identifier for the current Square Payment Form; used to prevent
* render multiple Payment Forms in a single document without running into duplicate
* DOM IDs.
* @private
*/
formId: null,

/**
* Renders the placeholder property to a data attribute so the parent component can
* inject it into the SqPaymentForm build cycle.
* @private
*/
placeholderAttribute: computed('placeholder', function() {
return this.placeholder || DEFAULT_PLACEHOLDER;
}),

/**
* Generated HTML ID referenced by the parent Payment Form component to reference an
* instance of this input.
* @private
*/
uniqueGiftCardInputId: computed('formId', function() {
return `sq-${this.formId}-gift-card-input`;
})
});
9 changes: 9 additions & 0 deletions addon/components/square-payment-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,15 @@ export default Component.extend({
assert('createPaymentRequest action is required for Masterpass.', false);
}
}

const giftCardInputEl = document.getElementById(`sq-${this.formId}-gift-card-input`);
if (giftCardInputEl) {
paymentFormConfig.giftCard = {
elementId: `sq-${this.formId}-gift-card-input`,
placeholder: giftCardInputEl.dataset.placeholder
};
}

const newPaymentForm = new SqPaymentForm(paymentFormConfig);
this.set('paymentForm', newPaymentForm);
newPaymentForm.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div id={{this.uniqueGiftCardInputId}} data-placeholder={{this.placeholderAttribute}}></div>
4 changes: 4 additions & 0 deletions addon/templates/components/square-payment-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
component "square-payment-form-credit-card-fields"
formId=this.formId
)
GiftCardInput=(
component "square-payment-form-gift-card-input"
formId=this.formId
)
GooglePayButton=(
component "square-payment-form-google-pay-button"
formId=this.formId
Expand Down
1 change: 1 addition & 0 deletions app/components/square-payment-form-gift-card-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-square-payment-form/components/square-payment-form-gift-card-input';
45 changes: 45 additions & 0 deletions tests/browser/gift-card.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
describe('Gift Card Form', () => {
beforeAll(async () => {
await page.goto('http://localhost:4200');
jest.setTimeout(30000);
});

it('can get a nonce', async () => {
await page.goto('http://localhost:4200/testing/gift-card');
await page.waitFor('iframe.square-payment-form-input');

const giftCardInputFrame = await page.$('iframe.square-payment-form-input');

// Allow iframes to fully load.
await page.waitFor(1000);

await giftCardInputFrame.focus('input');
await page.keyboard.type('7783 3200 0000 0000');

await page.click('.pay-now-button')
await page.waitFor('.nonce-response');

const errorsHandle = await page.$('.nonce-response__errors');
const nonceHandle = await page.$('.nonce-response__nonce');
const cardDataHandle = await page.$('.nonce-response__card-data');

await expect(
await errorsHandle.evaluate(node => node.innerText)
).toBe('null');

await expect(
await nonceHandle.evaluate(node => node.innerText)
).toMatch('cnon:');

await expect(
await cardDataHandle.evaluate(node => node.innerText)
).toMatch(`{
"digital_wallet_type": "NONE",
"card_brand": "SQUARE_GIFT_CARD",
"last_4": "0000",
"exp_month": 12,
"exp_year": 2050,
"billing_postal_code": "94117"
}`);
});
});
15 changes: 15 additions & 0 deletions tests/dummy/app/controllers/testing/gift-card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Controller from '@ember/controller';

export default Controller.extend({
nonceResponse: null,

actions: {
handleCardNonceResponse(errors, nonce, cardData) {
this.set('nonceResponse', {
errors: JSON.stringify(errors, null, ' '),
nonce,
cardData: JSON.stringify(cardData, null, ' ')
});
}
}
});
1 change: 1 addition & 0 deletions tests/dummy/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Router.map(function() {
this.route('testing', function() {
this.route('card-only');
this.route('card-only-sca');
this.route('gift-card');
});

this.route('examples', function() {
Expand Down
23 changes: 23 additions & 0 deletions tests/dummy/app/templates/testing/gift-card.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<SquarePaymentForm
@applicationId="sandbox-sq0idb-TsZm7fSKPzmM1HRI9zDfAQ"
@locationId="TC4Z3ZEBKRXRH"
@onCardNonceResponseReceived={{action "handleCardNonceResponse"}}
as |PaymentForm|>
<PaymentForm.GiftCardInput/>
<button class="pay-now-button" onclick={{action PaymentForm.requestCardNonce}}>
Pay Now
</button>
</SquarePaymentForm>

{{#if nonceResponse}}
<div class="nonce-response">
<h3>Results</h3>
<pre class="nonce-response__errors">{{nonceResponse.errors}}</pre>
<pre class="nonce-response__nonce">{{nonceResponse.nonce}}</pre>
<pre class="nonce-response__card-data">{{nonceResponse.cardData}}</pre>
<pre class="nonce-response__billing-contact">{{nonceResponse.billingContact}}</pre>
<pre class="nonce-response__shipping-contact">{{nonceResponse.shippingContact}}</pre>
<pre class="nonce-response__shipping-option">{{nonceResponse.shippingOption}}</pre>
<pre class="nonce-response__verification-token">{{nonceResponse.verificationToken}}</pre>
</div>
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | square-payment-form-gift-card-input', function(hooks) {
setupRenderingTest(hooks);

test('it renders', async function(assert) {
await render(hbs`<SquarePaymentFormGiftCardInput @formId="123"/>`);

const formElement = document.getElementById('sq-123-gift-card-input');
assert.ok(formElement, 'form ID should propagate to the postal code input');
});

test('it passes the placeholder data attribute through', async function(assert) {
await render(hbs`
<SquarePaymentFormGiftCardInput
@formId="123"
@placeholder="1234 5678 9012 3456"
/>
`);

const formElement = document.getElementById('sq-123-gift-card-input');
assert.equal(
formElement.dataset.placeholder,
'1234 5678 9012 3456',
'placeholder data attribute is passed through'
);
});
});

0 comments on commit fddc0f7

Please sign in to comment.