Skip to content

Commit a102c84

Browse files
authored
Stripe enhancements (codesandbox#2081)
* Update Stripe * Add coupon support * Add message about updating payment method * Update CircleCI cache * Update cache - again * 🧹 Cleanup Apply Coding Conventions, change noCoupon to hasCoupon All stylistic changes to be consistent with the latest refactors. No logic changes. * Style changes * TypeScript conversions
1 parent 89bf33d commit a102c84

File tree

29 files changed

+420
-240
lines changed

29 files changed

+420
-240
lines changed

.circleci/config.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ version: 2.1
66

77
aliases:
88
- &restore_repo_cache
9-
key: v17-repo-{{ .Environment.CIRCLE_SHA1 }}
9+
key: v20-repo-{{ .Environment.CIRCLE_SHA1 }}
1010

1111
- &save_repo_cache
12-
key: v17-repo-{{ .Environment.CIRCLE_SHA1 }}
12+
key: v20-repo-{{ .Environment.CIRCLE_SHA1 }}
1313
paths:
1414
- ~/codesandbox-client
1515

1616
- &restore_deps_cache
1717
keys:
18-
- v17-dependency-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
19-
- v17-dependency-cache-{{ .Branch }}
20-
- v17-dependency-cache
18+
- v20-dependency-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
19+
- v20-dependency-cache-{{ .Branch }}
20+
- v20-dependency-cache
2121

2222
- &save_deps_cache
23-
key: v17-dependency-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
23+
key: v20-dependency-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
2424
paths:
2525
- node_modules
2626
- packages/app/node_modules
@@ -39,29 +39,29 @@ aliases:
3939

4040
- &restore_standalone_deps_cache
4141
keys:
42-
- v17-standalone-dependency-cache-{{ .Branch }}-{{ checksum "standalone-packages/codesandbox-browserfs/yarn.lock" }}
43-
- v17-standalone-dependency-cache-{{ .Branch }}
44-
- v17-standalone-dependency-cache
42+
- v20-standalone-dependency-cache-{{ .Branch }}-{{ checksum "standalone-packages/codesandbox-browserfs/yarn.lock" }}
43+
- v20-standalone-dependency-cache-{{ .Branch }}
44+
- v20-standalone-dependency-cache
4545

4646
- &save_standalone_deps_cache
47-
key: v17-standalone-dependency-cache-{{ .Branch }}-{{ checksum "standalone-packages/codesandbox-browserfs/yarn.lock" }}
47+
key: v20-standalone-dependency-cache-{{ .Branch }}-{{ checksum "standalone-packages/codesandbox-browserfs/yarn.lock" }}
4848
paths:
4949
- standalone-packages/codesandbox-browserfs/node_modules
5050

5151
- &restore_prod_build_cache
52-
key: v17-prod-app-build-cache-master
52+
key: v20-prod-app-build-cache-master
5353

5454
- &restore_prod_cache
55-
key: v17-prod-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
55+
key: v20-prod-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
5656

5757
- &save_prod_app_cache
58-
key: v17-prod-app-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
58+
key: v20-prod-app-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
5959
paths:
6060
- ./packages/app/www
6161
- ./packages/homepage/.cache
6262

6363
- &save_prod_cache
64-
key: v17-prod-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
64+
key: v20-prod-build-cache-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }}
6565
paths:
6666
- ./www
6767

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import styled, { css } from 'styled-components';
2+
3+
export const LinkButton = styled.button`
4+
${({ theme }) => css`
5+
display: inline-block;
6+
padding: 0;
7+
margin: 0;
8+
border: none;
9+
background-color: transparent;
10+
color: ${theme.secondary};
11+
outline: none;
12+
text-decoration: underline;
13+
transition: 0.3s ease color;
14+
cursor: pointer;
15+
16+
&:hover {
17+
color: ${theme.secondary.lighten(0.1)};
18+
}
19+
`}
20+
`;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { LinkButton } from './LinkButton';

packages/app/src/app/components/Preview/DevTools/Tests/index.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,29 @@ type State = {
6767
watching: boolean;
6868
};
6969

70-
type SandboxMessage = { type: 'test' | 'done' } & (
71-
| InitializedTestsMessage
70+
type SandboxMessage =
7271
| CompilationDoneMessage
73-
| TestCountMessage
74-
| TotalTestStartMessage
75-
| TotalTestEndMessage
76-
| AddFileMessage
77-
| RemoveFileMessage
78-
| FileErrorMessage
79-
| DescribeStartMessage
80-
| DescribeEndMessage
81-
| AddTestMessage
82-
| TestStartMessage
83-
| TestEndMessage);
72+
| ({ type: 'test' } & (
73+
| InitializedTestsMessage
74+
| TestCountMessage
75+
| TotalTestStartMessage
76+
| TotalTestEndMessage
77+
| AddFileMessage
78+
| RemoveFileMessage
79+
| FileErrorMessage
80+
| DescribeStartMessage
81+
| DescribeEndMessage
82+
| AddTestMessage
83+
| TestStartMessage
84+
| TestEndMessage
85+
));
8486

8587
interface InitializedTestsMessage {
8688
event: messages.INITIALIZE;
8789
}
8890

8991
interface CompilationDoneMessage {
92+
type: 'done';
9093
compilatonError: boolean;
9194
}
9295

@@ -292,7 +295,7 @@ class Tests extends React.Component<DevToolProps, State> {
292295
);
293296
break;
294297
}
295-
case messages.REMOVE_FILE: {
298+
case 'remove_file': {
296299
this.setState(
297300
immer(this.state, state => {
298301
if (state.files[data.path]) {

packages/app/src/app/components/SubscribeForm/CheckoutForm/elements.js renamed to packages/app/src/app/components/SubscribeForm/CheckoutForm/elements.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,29 @@ import styled from 'styled-components';
22
import Input from '@codesandbox/common/lib/components/Input';
33

44
export const CardContainer = styled.div`
5-
background-color: rgba(0, 0, 0, 0.3);
65
padding: 0.5rem;
76
margin-top: 0.25rem;
7+
margin-bottom: 0.5rem;
88
border-radius: 4px;
9+
background-color: rgba(0, 0, 0, 0.3);
910
`;
1011

11-
export const NameInput = styled(Input)`
12+
export const StripeInput = styled(Input)`
1213
width: 100%;
13-
font-size: 0.875rem;
14+
height: 32.8px;
1415
padding: 0.5rem;
1516
margin-top: 0.25rem;
1617
margin-bottom: 0.5rem;
17-
height: 32.8px;
18+
font-size: 0.875rem;
1819
`;
1920

2021
export const ErrorText = styled.div`
22+
margin: 0.25rem 0;
2123
color: ${props => props.theme.red};
2224
font-size: 0.875rem;
23-
margin: 0.25rem 0;
2425
`;
2526

2627
export const Label = styled.label`
27-
color: rgba(255, 255, 255, 0.5);
2828
font-size: 0.875rem;
29+
color: rgba(255, 255, 255, 0.5);
2930
`;

packages/app/src/app/components/SubscribeForm/CheckoutForm/index.js renamed to packages/app/src/app/components/SubscribeForm/CheckoutForm/index.tsx

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,66 @@ import { injectStripe, CardElement } from 'react-stripe-elements';
33
import { Button } from '@codesandbox/common/lib/components/Button';
44
import { logError } from '@codesandbox/common/lib/utils/analytics';
55

6-
import { CardContainer, NameInput, ErrorText, Label } from './elements';
6+
import { CardContainer, StripeInput, ErrorText, Label } from './elements';
7+
8+
interface IStripe {
9+
createToken: (params: {
10+
name: string;
11+
}) => Promise<{
12+
token: { id: string };
13+
error?: Error;
14+
}>;
15+
}
716

8-
class CheckoutForm extends React.PureComponent {
9-
constructor(props) {
10-
super(props);
17+
interface Props {
18+
name: string;
19+
stripe: IStripe;
20+
buttonName: string;
21+
loadingText: string;
22+
isLoading: boolean;
23+
error?: Error;
24+
subscribe: (params: { token: string; coupon: string }) => void;
25+
hasCoupon?: boolean;
26+
}
1127

12-
this.state = {
13-
errors: {},
14-
name: props.name || '',
15-
};
16-
}
28+
interface State {
29+
errors: {
30+
name?: string;
31+
stripe?: string;
32+
};
33+
name: string;
34+
coupon: string;
35+
loading: boolean;
36+
}
37+
38+
class CheckoutForm extends React.PureComponent<Props, State> {
39+
state: State = {
40+
errors: {},
41+
name: this.props.name || '',
42+
coupon: '',
43+
loading: false,
44+
};
1745

18-
componentWillReceiveProps(nextProps) {
46+
componentWillReceiveProps(nextProps: Props) {
1947
if (nextProps.name !== this.props.name) {
2048
this.setState({ errors: {}, name: nextProps.name });
2149
}
2250
}
2351

24-
setName = e => {
52+
setName = (e: React.ChangeEvent<HTMLInputElement>) => {
2553
if (e) {
2654
this.setState({ errors: {}, name: e.target.value });
2755
}
2856
};
2957

30-
handleSubmit = async ev => {
31-
ev.preventDefault();
58+
setCoupon = (e: React.ChangeEvent<HTMLInputElement>) => {
59+
if (e) {
60+
this.setState({ errors: {}, coupon: e.target.value });
61+
}
62+
};
63+
64+
handleSubmit = async (e: React.FormEvent) => {
65+
e.preventDefault();
3266
if (!this.state.name) {
3367
return this.setState({ errors: { name: 'Please provide a name ' } });
3468
}
@@ -50,7 +84,10 @@ class CheckoutForm extends React.PureComponent {
5084
}
5185

5286
try {
53-
await this.props.subscribe(token.id);
87+
await this.props.subscribe({
88+
token: token.id,
89+
coupon: this.state.coupon,
90+
});
5491
} catch (e) {
5592
logError(e);
5693

@@ -68,7 +105,13 @@ class CheckoutForm extends React.PureComponent {
68105
};
69106

70107
render() {
71-
const { buttonName, loadingText, isLoading, error } = this.props;
108+
const {
109+
buttonName,
110+
loadingText,
111+
isLoading,
112+
error,
113+
hasCoupon = false,
114+
} = this.props;
72115
const { errors, loading: stateLoading } = this.state;
73116

74117
const loading = isLoading || stateLoading;
@@ -80,10 +123,10 @@ class CheckoutForm extends React.PureComponent {
80123
<Label>Cardholder Name</Label>
81124
{errors.name != null && <ErrorText>{errors.name}</ErrorText>}
82125
<div>
83-
<NameInput
126+
<StripeInput
84127
value={this.state.name}
85128
onChange={this.setName}
86-
error={errors.name}
129+
error={!!errors.name}
87130
placeholder="Please enter your name"
88131
/>
89132
</div>
@@ -96,6 +139,19 @@ class CheckoutForm extends React.PureComponent {
96139
/>
97140
</CardContainer>
98141

142+
{hasCoupon && (
143+
<>
144+
<Label>Coupon</Label>
145+
<div>
146+
<StripeInput
147+
value={this.state.coupon}
148+
onChange={this.setCoupon}
149+
placeholder="Coupon or Discount Code"
150+
/>
151+
</div>
152+
</>
153+
)}
154+
99155
<Button
100156
type="submit"
101157
disabled={loading}

packages/app/src/app/components/SubscribeForm/elements.js renamed to packages/app/src/app/components/SubscribeForm/elements.ts

File renamed without changes.

packages/app/src/app/components/SubscribeForm/index.js

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import { StripeProvider, Elements } from 'react-stripe-elements';
3+
import { STRIPE_API_KEY } from '@codesandbox/common/lib/utils/config';
4+
import CheckoutForm from './CheckoutForm';
5+
6+
import { Container } from './elements';
7+
8+
interface Props {
9+
name: string;
10+
subscribe: (params: { token: string; coupon: string }) => void;
11+
loadingText?: string;
12+
buttonName?: string;
13+
isLoading?: boolean;
14+
error?: string;
15+
hasCoupon?: boolean;
16+
}
17+
18+
const SubscribeForm = ({
19+
name,
20+
subscribe,
21+
loadingText = 'Creating Subscription...',
22+
buttonName = 'Subscribe',
23+
isLoading = false,
24+
error,
25+
hasCoupon,
26+
}: Props) => (
27+
<Container>
28+
<StripeProvider apiKey={STRIPE_API_KEY}>
29+
<Elements>
30+
<CheckoutForm
31+
buttonName={buttonName}
32+
loadingText={loadingText}
33+
subscribe={subscribe}
34+
name={name}
35+
isLoading={isLoading}
36+
error={error}
37+
hasCoupon={hasCoupon}
38+
/>
39+
</Elements>
40+
</StripeProvider>
41+
</Container>
42+
);
43+
44+
export default SubscribeForm;

0 commit comments

Comments
 (0)