Skip to content

Commit

Permalink
Adds useCheckoutPricing hook
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrudner committed Mar 2, 2020
1 parent d292550 commit 4bc86bf
Show file tree
Hide file tree
Showing 13 changed files with 1,397 additions and 84 deletions.
33 changes: 33 additions & 0 deletions README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,38 @@ export function MyPaymentForm (props) {

With that, we have implemented the essential components of a payment form using react-recurly. The tokens generated above may be used on any `billing_info` object in the [Recurly API][docs-recurly-api].

## Additional Usage

React-recurly also includes a [useCheckoutPricing][docs-hook-use-checkout-pricing] custom hook for generating a pricing preview before checking out.

```js
import { useCheckoutPricing, RecurlyProvider } from '@recurly/react-recurly';

function PricingPreview () {
const initialPricingInput = {
subscriptions: [
{
plan: 'my-plan'
}
]
};

const [{ price, loading }, setCheckoutPricing] = useCheckoutPricing(initialPricingInput);

if (!loading) {
return <div>{price.now.subtotal}</div>
};
};

export default function MyApp () {
<RecurlyProvider>
<PricingPreview />
</RecurlyProvider>
};
```

For more information on `useCheckoutPricing`, view the [implementation guide here][docs-hook-use-checkout-pricing].

##### More resources

* [Documentation & Reference][docs]
Expand All @@ -150,6 +182,7 @@ MIT
[docs-component-elements]: https://recurly.github.io/react-recurly/?path=/docs/components-elements--page
[docs-component-card-element]: https://recurly.github.io/react-recurly/?path=/docs/components-cardelement--default
[docs-hook-use-recurly]: https://recurly.github.io/react-recurly/?path=/docs/hooks-userecurly--page
[docs-hook-use-checkout-pricing]: https://recurly.github.io/react-recurly/?path=/docs/hooks-usecheckoutpricing--page

[docs-recurly-js]: https://developers.recurly.com/reference/recurly-js/
[docs-recurly-js-elements]: https://developers.recurly.com/reference/recurly-js/#elements
Expand Down
8 changes: 6 additions & 2 deletions demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,29 @@
margin: 20px 0;
padding: 0;
}
h2 {
h2, h3 {
color: #555;
padding: 0;
margin: 0;
font-size: 1.2em;
font-weight: 400;
margin-bottom: 0.5em;
}
h3 {
font-size: 1em;
}
p {
font-size: 0.8em;
}
input {
input, select {
border: 1px solid #d7d7d9;
color: rgb(84, 84, 87);
margin: 0 1em 0 0;
height: 2em;
box-sizing: border-box;
outline: 0;
padding: 0 8px 0 8px;
width: 140px;
}
button {
background: #99c4c7;
Expand Down
163 changes: 163 additions & 0 deletions demo/src/checkout-pricing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import React, { useState, useEffect } from 'react';
import { Elements, RecurlyProvider, useCheckoutPricing } from '@recurly/react-recurly';

export function CheckoutPricing () {
return (
<RecurlyProvider publicKey={process.env.REACT_APP_RECURLY_PUBLIC_KEY}>
<Elements>
<CheckoutPricingForm />
</Elements>
</RecurlyProvider>
);
};

function CheckoutPricingForm () {
const [recurlyError, setRecurlyError] = useState(null);
const [pricingFormState, setPricingFormState] = useState({
plan: '',
planQuantity: '',
itemCode: '',
itemQuantity: '',
coupon: '',
giftCard: '',
currency: '',
billingCountry: '',
billingPostalCode: '',
billingVatNumber: '',
shippingCountry: '',
shippingPostalCode: '',
shippingVatNumber: ''
});

const [{ price, loading }, setPricing] = useCheckoutPricing(null, setRecurlyError);
const showPrice = !loading && !recurlyError;

function handleChange (name, value) {
const newState = { ...pricingFormState, [name]: value };
setPricingFormState(newState);
};

useEffect(() => {
setRecurlyError(null);

const subscriptions = [{
plan: pricingFormState.plan,
quantity: pricingFormState.planQuantity
}];
const adjustments = [{
itemCode: pricingFormState.itemCode,
quantity: pricingFormState.itemQuantity
}];
const address = {
country: pricingFormState.billingCountry,
postal_code: pricingFormState.billingPostalCode
};
const shippingAddress = {
country: pricingFormState.shippingCountry,
postal_code: pricingFormState.shippingPostalCode
};

setPricing({ ...pricingFormState, subscriptions, adjustments, address, shippingAddress });
}, [pricingFormState, setPricing]);

return (
<div className="DemoSection">
<div>
<h3>Subscription</h3>
<select value={pricingFormState.plan} onChange={e => handleChange('plan', e.target.value)}>
<option value="">Select a plan</option>
<option value="basic">Basic</option>
<option value="advanced">Advanced</option>
<option value="error">Error</option>
</select>
<input
type="number"
value={pricingFormState.planQuantity}
onChange={e => handleChange('planQuantity', e.target.value)}
placeholder="Plan quantity"
min="0"
/>
<select
type="text"
value={pricingFormState.currency}
onChange={e => handleChange('currency', e.target.value)}
placeholder="Currency"
>
<option value="USD">USD</option>
<option value="JPY">JPY</option>
</select>
</div>
<div style={{ marginTop: '10px' }}>
<h3>Item/adjustments</h3>
<input
type="text"
value={pricingFormState.itemCode}
onChange={e => handleChange('itemCode', e.target.value)}
placeholder="Item code"
/>
<input
type="number"
value={pricingFormState.itemQuantity}
onChange={e => handleChange('itemQuantity', e.target.value)}
placeholder="Quantity"
min="0"
/>
</div>
<div style={{ marginTop: '10px' }}>
<h3>Coupon and giftcard</h3>
<input
type="text"
value={pricingFormState.coupon}
onChange={e => handleChange('coupon', e.target.value)}
placeholder="Coupon"
/>
<input
type="text"
value={pricingFormState.giftCard}
onChange={e => handleChange('giftCard', e.target.value)}
placeholder="Gift card"
/>
</div>
<div style={{ marginTop: '10px' }}>
<h3>Billing address</h3>
<input
type="text"
value={pricingFormState.billingCountry}
onChange={e => handleChange('billingCountry', e.target.value)}
placeholder="Country"
/>
<input
type="text"
value={pricingFormState.billingPostalCode}
onChange={e => handleChange('billingPostalCode', e.target.value)}
placeholder="Postal code"
/>
</div>
<div style={{ marginTop: '10px' }}>
<h3>Shipping address</h3>
<input
type="text"
value={pricingFormState.shippingCountry}
onChange={e => handleChange('shippingCountry', e.target.value)}
placeholder="Country"
/>
<input
type="text"
value={pricingFormState.shippingPostalCode}
onChange={e => handleChange('shippingPostalCode', e.target.value)}
placeholder="Postal code"
/>
</div>
<div style={{ marginTop: '10px' }}>
{recurlyError ? <span style={{ color: 'red' }}>{recurlyError.message}</span> : ''}
{showPrice ? (
<span>
{'Subtotal: '}
<span>{`${price.currency.symbol}${price.now.subtotal}`}</span>
</span>
) : null}
{loading && 'Loading'}
</div>
</div>
);
};
4 changes: 4 additions & 0 deletions demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { render } from 'react-dom';
import { CardElementDemo } from './card-element-demo';
import { IndividualCardElementsDemo } from './individual-card-elements-demo';
import { ThreeDSecureDemo } from './three-d-secure-demo';
import { CheckoutPricing } from './checkout-pricing';

function App () {
return (
Expand All @@ -31,6 +32,9 @@ function App () {

<h2>3D Secure</h2>
<ThreeDSecureDemo />

<h2>Checkout Pricing</h2>
<CheckoutPricing />
</div>
);
};
Expand Down
Loading

0 comments on commit 4bc86bf

Please sign in to comment.