﻿WEBVTT

1
00:00:10.640 --> 00:00:11.473
<v 0>Hi everyone.</v>

2
00:00:13.380 --> 00:00:16.320
Welcome to day two of Stripe Sessions.

3
00:00:17.480 --> 00:00:18.480
This breakout session,

4
00:00:18.580 --> 00:00:22.720
we're going to be covering how to extend Stripe to look and feel like your

5
00:00:22.760 --> 00:00:24.500
business. My name is Brooks.

6
00:00:24.700 --> 00:00:27.740
I lead engineering for a group at Stripe known as extensibility platform.

7
00:00:28.220 --> 00:00:29.380
I'm joined by Mike North here,

8
00:00:29.480 --> 00:00:33.440
who is the tech lead for Stripe's developer platform. But today,

9
00:00:33.700 --> 00:00:35.120
at least for the sake of this talk,

10
00:00:35.280 --> 00:00:38.400
we're going to do a little bit of role playing. Instead of working at Stripe,

11
00:00:38.640 --> 00:00:43.100
Mike and I are going to be the proprietors of a wildly successful car rental

12
00:00:43.160 --> 00:00:47.220
company that's totally not fictitious at all, known as Rocket Rides.

13
00:00:48.140 --> 00:00:49.160
Over the course of this talk,

14
00:00:49.440 --> 00:00:53.640
we are going to cover a few new products and a few new features that launched at

15
00:00:53.700 --> 00:00:55.140
Stripe Sessions this week.

16
00:00:55.620 --> 00:00:59.240
And we're going to show you how we have built a full stack Stripe-hosted

17
00:00:59.340 --> 00:01:04.340
application for the exact needs of Rocket Rides.
I'll start by

18
00:01:04.400 --> 00:01:07.640
giving you a quick introduction into the business of car rentals.

19
00:01:08.120 --> 00:01:11.900
There's really only three core concepts that you have to worry about: drivers,

20
00:01:12.580 --> 00:01:17.560
vehicles, and rentals. Now, when we want to integrate our business with Stripe,

21
00:01:17.840 --> 00:01:22.280
the first thing that we have to do is sit down and build a mental mapping

22
00:01:22.820 --> 00:01:27.540
between the core concepts of our business and the common core of every other

23
00:01:27.580 --> 00:01:28.580
business and Stripe.

24
00:01:29.680 --> 00:01:33.620
We familiarize ourselves with these abstractions by reading documentation,

25
00:01:34.080 --> 00:01:36.400
watching videos, or increasingly these days,

26
00:01:36.480 --> 00:01:38.640
vibe coding our way through a solution.

27
00:01:39.520 --> 00:01:42.860
And the result of our work is a sort of mental Rosetta Stone.

28
00:01:43.180 --> 00:01:47.760
It's a translation between the language of our business and the common core of

29
00:01:47.900 --> 00:01:51.420
every other running on Stripe.
For example, at Rocket Rides,

30
00:01:51.500 --> 00:01:55.200
we refer to the folks that are renting these cars as drivers.

31
00:01:55.720 --> 00:01:59.740
So maybe our mental model should be that we map the concept of a driver to which

32
00:01:59.760 --> 00:02:03.120
Stripe calls a customer. And I guess if you squint,

33
00:02:03.360 --> 00:02:07.580
maybe you could map the concept of a vehicle to what Stripe calls a product

34
00:02:08.380 --> 00:02:12.920
and a rental, which is ultimately how we generate revenue to a payment.

35
00:02:14.400 --> 00:02:16.320
But we're not alone in this exercise.

36
00:02:16.620 --> 00:02:20.640
There are literally hundreds of thousands of merchants all going through this

37
00:02:20.720 --> 00:02:25.400
exact same exercise of building a mental mapping between their business

38
00:02:25.860 --> 00:02:29.320
and Stripe. And unsurprisingly, there isn't always a perfect fit.

39
00:02:30.120 --> 00:02:34.520
And that's because your business is unique. And so to solve this problem,

40
00:02:35.000 --> 00:02:39.380
we have to extend Stripe beyond what comes out of the box.

41
00:02:40.800 --> 00:02:44.800
Let's start by taking a look at a mock-up that our designer put together here at

42
00:02:44.840 --> 00:02:48.920
Rocket Rides that's leveraging some of the new features that were launched this

43
00:02:48.980 --> 00:02:50.100
week at Sessions.

44
00:02:51.280 --> 00:02:55.960
So what we're looking at here is a custom overview page of Rocket Rides right in

45
00:02:55.980 --> 00:02:58.720
the Stripe Dashboard. On this page,

46
00:02:58.780 --> 00:03:02.560
our entire team can see important top-line metrics like how many vehicles in the

47
00:03:02.640 --> 00:03:05.540
fleet are currently available to be rented.

48
00:03:05.540 --> 00:03:09.140
There's charts that our finance team uses to see revenue per vehicle.

49
00:03:09.660 --> 00:03:13.040
They find this super helpful in forecasting the next set of cars that they're

50
00:03:13.080 --> 00:03:13.913
going to purchase.

51
00:03:15.040 --> 00:03:19.840
Our team at the front desk can quickly access upcoming rentals as well as take

52
00:03:19.940 --> 00:03:23.700
actions like mark the rental as returned when they get the keys back from the

53
00:03:23.740 --> 00:03:25.780
driver. Now,

54
00:03:26.040 --> 00:03:30.240
all of the functionality that's in these mock-ups can be broken up into three

55
00:03:30.320 --> 00:03:34.260
different parts of Stripe's extensibility platform: data,

56
00:03:34.980 --> 00:03:37.320
user interfaces, and business logic.

57
00:03:38.880 --> 00:03:42.500
Data represents the language of our business. In our case, it's the nouns.

58
00:03:42.620 --> 00:03:46.780
It's a driver, a vehicle, maybe one day in the future, a damage report.

59
00:03:48.220 --> 00:03:51.780
UI are the set of tools that we use to visualize this information in the Stripe

60
00:03:51.800 --> 00:03:54.060
Dashboard. And finally,

61
00:03:54.380 --> 00:03:58.800
logic are the daily actions that we take and the things that make our business

62
00:03:59.060 --> 00:04:03.840
unique.
These three core concepts work very well together when you package

63
00:04:03.900 --> 00:04:06.160
them in a Stripe app.

64
00:04:06.160 --> 00:04:09.540
And these apps can be installed into your account and made private

65
00:04:09.980 --> 00:04:12.860
for any employee, in our case, at Rocket Rides. to see.

66
00:04:13.800 --> 00:04:17.060
So let's take a much deeper dive into each of these sections and we'll start

67
00:04:17.300 --> 00:04:19.780
with data. This week,

68
00:04:20.060 --> 00:04:23.340
Stripe announced the private preview of custom objects.

69
00:04:24.080 --> 00:04:27.540
Custom objects put you behind the steering wheel in designing the core

70
00:04:27.560 --> 00:04:32.480
abstractions of your business. They have first-class APIs and SDKs in

71
00:04:34.320 --> 00:04:37.980
every programming language that Stripe has a programming language for.

72
00:04:39.440 --> 00:04:40.273
Before today,

73
00:04:40.480 --> 00:04:44.740
the core objects of Rocket Rides were a little bit tricky to model and store

74
00:04:44.760 --> 00:04:46.660
within Stripe. But actually,

75
00:04:46.900 --> 00:04:51.240
drivers are close enough to something that Stripe already has something for.

76
00:04:51.840 --> 00:04:55.720
They're really just customers with some driver-specific metadata.

77
00:04:56.580 --> 00:04:57.413
That said,

78
00:04:57.520 --> 00:05:01.840
there's no perfect equivalent for the concept of a vehicle and a rental,

79
00:05:02.380 --> 00:05:06.040
and that's exactly the problem that custom objects set out to solve.

80
00:05:07.640 --> 00:05:10.420
Vehicles have attributes like a make and a model,

81
00:05:10.720 --> 00:05:12.280
the year that it was manufactured,

82
00:05:12.360 --> 00:05:16.780
and a VIN or vehicle identification number that has to be exactly 17 characters

83
00:05:16.820 --> 00:05:21.560
long. The second core object of Rocket Rides is a rental.

84
00:05:22.240 --> 00:05:24.900
Now, not only is this the primary thing that we sell,

85
00:05:25.220 --> 00:05:30.100
but it also represents the connection or edge to a vehicle and to a

86
00:05:30.160 --> 00:05:34.560
driver. On it, we store information like when the rental begins,

87
00:05:35.160 --> 00:05:36.140
as well as a status.

88
00:05:36.300 --> 00:05:40.220
And the status indicates if it is awaiting inspection for damages or if it's

89
00:05:40.240 --> 00:05:44.020
been finalized and ready to be invoiced to our customers.

90
00:05:45.600 --> 00:05:48.780
We define custom objects using an object definition.

91
00:05:49.540 --> 00:05:53.740
You can think of these as a schema or blueprint that every custom object that

92
00:05:53.780 --> 00:05:55.200
you create will conform to.

93
00:05:56.200 --> 00:06:00.420
It's a TypeScript file that includes the object's fields, their types,

94
00:06:00.540 --> 00:06:01.400
validations,

95
00:06:01.460 --> 00:06:05.800
and then those relationships to other custom objects.
So there's a lot of code

96
00:06:05.820 --> 00:06:08.240
on the screen. Let's break this down section by section.

97
00:06:09.780 --> 00:06:13.800
We specify the fields of our custom object using an interface.

98
00:06:14.680 --> 00:06:18.000
And this interface not only defines the name of the attributes,

99
00:06:18.740 --> 00:06:20.940
but also their types. This is TypeScript after all.

100
00:06:21.420 --> 00:06:26.200
And so we can use built-in primitives like a string, a number, a Boollean,

101
00:06:26.760 --> 00:06:27.960
or what we see on the screen here,

102
00:06:28.240 --> 00:06:32.400
a date or an enum to reflect the different states of that status.

103
00:06:33.920 --> 00:06:38.480
There's also a standard library provided by Stripe that has even more types like

104
00:06:38.520 --> 00:06:42.860
a monetary amount, a positive integer, or a decimal. Now,

105
00:06:42.920 --> 00:06:46.860
by authoring your custom objects with a powerful type system like this,

106
00:06:47.140 --> 00:06:51.560
you can ensure that you have consistency and correctness anytime that a custom

107
00:06:51.700 --> 00:06:54.040
object is added or updated.

108
00:06:55.520 --> 00:06:58.280
You'll notice that there's another built-in type known as a ref,

109
00:06:58.980 --> 00:07:02.720
and references allow you to define relationships to other

110
00:07:02.900 --> 00:07:04.260
objects. And

111
00:07:05.800 --> 00:07:08.660
these relationships don't just have to be to other custom objects,

112
00:07:09.060 --> 00:07:13.240
but they also can be to core Stripe objects as well, like a customer, a price,

113
00:07:13.400 --> 00:07:14.233
or a payment.

114
00:07:15.980 --> 00:07:20.120
Custom object definitions also support validations that can go beyond what you

115
00:07:20.140 --> 00:07:22.420
can articulate in the type system, in this case,

116
00:07:22.800 --> 00:07:25.340
maximum value using comment tags.

117
00:07:27.180 --> 00:07:30.160
So we've defined the shape and the schema of our object definition,

118
00:07:30.240 --> 00:07:32.640
but there's one key thing that I haven't talked about yet.

119
00:07:33.500 --> 00:07:36.100
It's that custom object decorator at the bottom.

120
00:07:36.980 --> 00:07:39.560
This decorator is an incredibly powerful concept.

121
00:07:39.640 --> 00:07:42.060
When you annotate your TypeScript class with this,

122
00:07:42.700 --> 00:07:47.580
it tells Stripe to automatically provision a set of APIs that gives us the

123
00:07:47.660 --> 00:07:50.480
ability to create, read, update,

124
00:07:50.540 --> 00:07:54.060
and destroy instances of these custom objects.

125
00:07:54.680 --> 00:07:55.960
These are known as CRUD APIs,

126
00:07:56.240 --> 00:07:59.200
and they live in Stripe literally at api.stripe.com,

127
00:07:59.780 --> 00:08:03.260
which means that you can interact with them just like you would any other Stripe

128
00:08:03.340 --> 00:08:06.080
API resource. OK.

129
00:08:06.340 --> 00:08:09.540
So we have a rough understanding of what an object definition is and how they

130
00:08:09.600 --> 00:08:11.220
work. Mike, as you know,

131
00:08:11.300 --> 00:08:14.900
we have an old legacy application that we use to run the operations of

132
00:08:14.980 --> 00:08:19.000
Rocket Rides. I feel like a better place might actually now be on Stripe.

133
00:08:19.060 --> 00:08:20.660
How would we go about doing that?

134
00:08:20.720 --> 00:08:24.820
<v 1>We're going to build a Stripe app just for Rocket Rides and deploy it to our own</v>

135
00:08:24.860 --> 00:08:25.693
account.

136
00:08:26.620 --> 00:08:30.680
So the first thing we're going to do is generate a new custom object definition.

137
00:08:31.120 --> 00:08:35.500
We're going to reach for the Stripe CLI and run a new command called stripe

138
00:08:36.140 --> 00:08:38.480
generate custom-object,

139
00:08:38.800 --> 00:08:41.340
and then we're going to name our custom object vehicle.

140
00:08:42.880 --> 00:08:46.240
What's happening behind the scenes is we're getting some scaffolding,

141
00:08:46.440 --> 00:08:50.840
some files are created for us. And this YAML file we're looking at here,

142
00:08:51.200 --> 00:08:52.640
our Stripe app manifest,

143
00:08:53.240 --> 00:08:56.720
is already set up so it knows where this object definition is.

144
00:08:57.760 --> 00:09:00.640
Let's click on this and see what was generated for us.

145
00:09:01.480 --> 00:09:05.440
So we can see we have this object.ts file,

146
00:09:06.040 --> 00:09:07.160
and alongside it,

147
00:09:07.520 --> 00:09:11.280
we have a set of tests that are ready to go if we wanted to add tests to this.

148
00:09:12.360 --> 00:09:15.760
Let's look a little closer at what's going on here.

149
00:09:15.760 --> 00:09:18.760
Our vehicle class extends from base object.

150
00:09:19.280 --> 00:09:24.280
This is what gives the vehicle object everything Stripe expects to

151
00:09:24.340 --> 00:09:26.360
see from any API resource.

152
00:09:26.520 --> 00:09:30.120
This includes a unique ID and a creation timestamp.

153
00:09:31.120 --> 00:09:34.560
Here's that custom object decorator that Brooks was mentioning,

154
00:09:35.000 --> 00:09:39.840
and this is what tells Stripe to provision these read and write API endpoints.

155
00:09:40.000 --> 00:09:44.180
And you can see that we get some webhook events set up for us as well,

156
00:09:44.420 --> 00:09:47.080
so we can listen for when new vehicles are created, when they're changed,

157
00:09:47.240 --> 00:09:49.520
and when they're removed.
Now,

158
00:09:49.760 --> 00:09:53.920
if we look at this vehicle field's interface that's passed into this space

159
00:09:54.040 --> 00:09:54.873
object type,

160
00:09:55.440 --> 00:10:00.400
this is where we're going to define what a vehicle is

161
00:10:01.000 --> 00:10:03.840
in Rocket Rides, like what makes a vehicle a vehicle.

162
00:10:04.940 --> 00:10:08.260
And we got a name field as part of the generated code,

163
00:10:08.320 --> 00:10:13.240
but let's change this to make it a bit more interesting. Every vehicle has make,

164
00:10:13.820 --> 00:10:17.300
a model, and a year.

165
00:10:17.300 --> 00:10:18.880
A year probably shouldn't be a string.

166
00:10:19.020 --> 00:10:20.740
We can make it a positive integer,

167
00:10:22.600 --> 00:10:26.280
and that's going to come from our standard library of types here that's included

168
00:10:26.320 --> 00:10:31.280
in the extensibility SDK. Every vehicle has a VIN number, which is a string,

169
00:10:32.100 --> 00:10:36.660
and we can give that a display name. So display name.

170
00:10:37.980 --> 00:10:39.720
So everybody understands what a VIN number is.

171
00:10:40.940 --> 00:10:43.500
Vehicle identification number.

172
00:10:44.400 --> 00:10:46.320
A little spelling here there.

173
00:10:47.960 --> 00:10:51.300
Brooks mentioned 17 characters, right? That's how long this needs to be.

174
00:10:51.480 --> 00:10:55.940
So we can say min length, 17, max length,

175
00:10:56.840 --> 00:11:00.560
17. And there we go. Just a couple more fields here.

176
00:11:00.640 --> 00:11:03.340
We can say every vehicle has a license plate

177
00:11:05.380 --> 00:11:08.040
and that's a string, and an odometer reading,

178
00:11:09.720 --> 00:11:14.640
which is another positive integer. P-O-S-I. There you go.

179
00:11:14.880 --> 00:11:18.120
So great. We're done with our vehicle object definition.

180
00:11:18.680 --> 00:11:23.320
Let's generate one more for rental.
So we're reaching for the Stripe

181
00:11:23.360 --> 00:11:26.980
CLI again, and we're running stripe generate custom-object

182
00:11:27.200 --> 00:11:31.700
rental. This is doing the same thing behind the scenes.

183
00:11:31.700 --> 00:11:35.280
There's our automatic change to the app manifest. We'll just click in.

184
00:11:35.860 --> 00:11:37.760
And like any good cooking show,

185
00:11:38.000 --> 00:11:41.080
I've got something in the oven that's already baked, so we'll say Z-R-E-N.

186
00:11:42.280 --> 00:11:43.113
There we go.

187
00:11:44.560 --> 00:11:48.220
So the interesting thing about this that we didn't see on vehicle is

188
00:11:48.740 --> 00:11:52.020
relationships. We've got a field on rental called vehicle,

189
00:11:52.160 --> 00:11:55.860
and this refers to the vehicle type that we were just working in.

190
00:11:57.020 --> 00:12:01.500
We've also got a field here called driver. Now, to Rocket Rides, we say driver,

191
00:12:02.200 --> 00:12:04.080
in Stripe's world, that's a customer.

192
00:12:04.560 --> 00:12:06.640
And so we're free to name that field whatever we want,

193
00:12:06.700 --> 00:12:11.480
but this is a v1 customer that you've been able to find in our API for years and

194
00:12:11.520 --> 00:12:13.020
in the Dashboard as customers.

195
00:12:13.540 --> 00:12:18.300
We've got start and end dates for the duration of the rental and a pickup and

196
00:12:18.320 --> 00:12:22.180
drop off location and then a status field so we can understand what state this

197
00:12:22.240 --> 00:12:23.100
rental is in.

198
00:12:23.100 --> 00:12:27.660
So just making sure everything's saved.
Now we need to tell Stripe to

199
00:12:27.720 --> 00:12:29.780
provision these new APIs for us.

200
00:12:30.000 --> 00:12:33.800
So we're going to upload our Stripe app again using the Stripe CLI,

201
00:12:34.080 --> 00:12:36.680
and that's stripe apps upload.

202
00:12:38.260 --> 00:12:42.080
What's happening behind the scenes is we're analyzing these TypeScript files and

203
00:12:42.120 --> 00:12:43.780
creating an open API spec,

204
00:12:44.240 --> 00:12:48.700
sending that over to Stripe so it knows exactly the shapes of vehicle and

205
00:12:48.760 --> 00:12:49.120
rental.

206
00:12:49.120 --> 00:12:54.000
It knows which APIs to set up and we can play with those APIs right

207
00:12:54.020 --> 00:12:57.840
now. So we're going to use stripe get,

208
00:12:58.120 --> 00:13:02.960
which you can think of as kind of like using curl to hit an API

209
00:13:03.020 --> 00:13:05.600
endpoint, but this sets all those headers for you that you need,

210
00:13:06.100 --> 00:13:09.960
takes care of auth. So we're going to hit /v2/extend/

211
00:13:11.280 --> 00:13:16.220
objects/vehicles. Now,

212
00:13:16.320 --> 00:13:19.120
if you've used list endpoints at Stripe before,

213
00:13:20.260 --> 00:13:21.920
this is the kind of response that comes back.

214
00:13:22.040 --> 00:13:26.620
You get a data field that's an array, and that's where we would see vehicles,

215
00:13:26.680 --> 00:13:30.040
but we just stood this API up, so there's nothing here for us yet.

216
00:13:31.140 --> 00:13:34.720
Let's create a vehicle. Now here,

217
00:13:35.340 --> 00:13:38.880
we're using the Stripe CLI again. We're making a post request this time,

218
00:13:38.940 --> 00:13:42.500
and here's the JSON that we're passing along. Let's try this.

219
00:13:43.480 --> 00:13:46.120
And it turns out our VIN number was too short,

220
00:13:46.180 --> 00:13:50.440
and we're getting a nice actionable error back, so we can correct that.

221
00:13:52.880 --> 00:13:55.920
That looks like a successful create to me, and just for fun,

222
00:13:55.980 --> 00:14:00.260
we'll read that list again. And there's our, hopefully, yeah,

223
00:14:00.520 --> 00:14:03.440
there's our data array that contains our DeLorean.

224
00:14:04.080 --> 00:14:07.560
So we started with a mostly empty app.

225
00:14:08.000 --> 00:14:11.200
We created our own vehicle and rental object definitions.

226
00:14:11.260 --> 00:14:13.480
We defined our own well-typed fields.

227
00:14:13.560 --> 00:14:17.480
We got clear errors back when the data was wrong,

228
00:14:17.920 --> 00:14:21.120
and then we used the Stripe CLI to deploy our Stripe app,

229
00:14:21.200 --> 00:14:26.080
which included the object definitions to Stripe so that Stripe created

230
00:14:26.100 --> 00:14:27.560
these new API endpoints for us.

231
00:14:27.840 --> 00:14:32.280
And this is a huge part of the data pillar of Stripe's new extensibility

232
00:14:32.340 --> 00:14:33.173
features.

233
00:14:34.360 --> 00:14:37.470
<v 0>Amazing. Thank you, Mike. So custom objects...</v>

234
00:14:38.620 --> 00:14:39.520
It's very cool.

235
00:14:42.220 --> 00:14:47.020
So custom objects let you extend Stripe with the core objects of your business.

236
00:14:47.480 --> 00:14:48.313
When you do that,

237
00:14:48.420 --> 00:14:52.090
you benefit from the broader Stripe developer experience that Mike just showed

238
00:14:52.140 --> 00:14:55.640
us.
Let's take a look at the next part of the extensibility story,

239
00:14:56.370 --> 00:14:58.430
user interfaces. This week,

240
00:14:58.610 --> 00:15:01.770
Stripe released a new set of features for UI extensions.

241
00:15:02.560 --> 00:15:05.970
UI extensions are the way that you can build your own user interfaces that live

242
00:15:06.010 --> 00:15:07.690
directly in the Stripe Dashboard.

243
00:15:08.750 --> 00:15:12.890
They're built with a flexible design system of components like forms, buttons,

244
00:15:12.990 --> 00:15:14.910
dropdowns, and icons. And luckily for me,

245
00:15:15.190 --> 00:15:19.450
they have all been vetted and built by Stripe's design team. Now,

246
00:15:19.510 --> 00:15:21.230
when you want to build a UI extension,

247
00:15:21.290 --> 00:15:24.670
you start by identifying their viewports.

248
00:15:24.790 --> 00:15:28.330
These are the sections of the Stripe Dashboard where you want your UI to appear.

249
00:15:28.970 --> 00:15:29.810
So say for example,

250
00:15:29.870 --> 00:15:33.250
you want to see a side-by-side view of a customer and some contextual

251
00:15:33.290 --> 00:15:36.310
information from your business about them. To do this,

252
00:15:36.610 --> 00:15:40.650
you would build a UI extension that plugs into the customer detail viewport.

253
00:15:41.370 --> 00:15:46.090
This will show up in a sidebar anytime that you're looking at a customer. Now,

254
00:15:46.290 --> 00:15:47.790
UI extensions are not a new concept.

255
00:15:47.930 --> 00:15:51.770
They've actually been around since 2021 since Stripe released them with the

256
00:15:51.850 --> 00:15:53.390
initial launch of Stripe Apps,

257
00:15:53.470 --> 00:15:58.290
but this week they're becoming even more powerful with even more

258
00:15:58.330 --> 00:16:01.610
flexibility and real estate within the Stripe Dashboard.

259
00:16:02.570 --> 00:16:06.950
Stripe also released a new layout for overview pages and several new UI

260
00:16:06.990 --> 00:16:10.890
components that make it easier to visualize things like charts and tables.

261
00:16:11.730 --> 00:16:15.850
There's also support for tabs where you can navigate directly from the URL.

262
00:16:16.870 --> 00:16:20.890
Now, UI extensions are a powerful primitive entirely on their own,

263
00:16:21.190 --> 00:16:25.870
but the real magic happens when you pair them with custom objects.
All right,

264
00:16:26.130 --> 00:16:28.390
so we have this beautiful mock-up from our designer.

265
00:16:28.690 --> 00:16:31.910
We have created our custom object definitions. Mike,

266
00:16:31.970 --> 00:16:33.510
what's it going to take to wire all of this up?

267
00:16:34.530 --> 00:16:39.070
<v 1>About three Red Bulls, half a pizza, and 150,000 AI tokens.</v>

268
00:16:39.290 --> 00:16:40.123
So yeah,

269
00:16:40.390 --> 00:16:45.230
let's start with a Stripe app that was built by our Claude design agent.

270
00:16:45.330 --> 00:16:49.290
So we use some of the agent skills that were included with the extensibility SDK

271
00:16:50.350 --> 00:16:54.570
and use some of these beautiful new components for data visualization.

272
00:16:54.630 --> 00:16:58.590
We have a little data view over here. We can click on it for more details,

273
00:16:58.670 --> 00:17:01.350
really taking advantage of that full-page viewport here.

274
00:17:02.270 --> 00:17:04.890
We also have a vehicle and a rental tab,

275
00:17:05.430 --> 00:17:08.770
but it looks to me like we have a lot of placeholder data here.

276
00:17:08.890 --> 00:17:11.610
Claude gave everything the same ID. That doesn't seem right.

277
00:17:12.130 --> 00:17:16.950
So what we need to do is find a way to bring our new

278
00:17:17.610 --> 00:17:22.460
custom object APIs into this UI extension so it's powered by our

279
00:17:22.530 --> 00:17:27.450
vehicle and rental APIs that we just stood up.
So let's go back to our

280
00:17:27.510 --> 00:17:29.770
Stripe app and we're back in the manifest here.

281
00:17:29.930 --> 00:17:33.870
And this is the section of our Stripe app that deals with UI extensions.

282
00:17:33.930 --> 00:17:38.810
We can see here we are placing our own component that's called "Full Page"

283
00:17:39.410 --> 00:17:43.480
in this full-page viewport. So let's drill into this full-page component.

284
00:17:44.330 --> 00:17:48.270
And here we can see we have a set of tabs. Here's our overview tab,

285
00:17:48.850 --> 00:17:50.590
here's a body of the overview tab,

286
00:17:50.790 --> 00:17:52.950
and here's our vehicles tab and our rentals tab.

287
00:17:53.030 --> 00:17:56.410
So I happen to know that this component here and this component here,

288
00:17:56.870 --> 00:18:01.510
those are this lower area that's visible when you click the tab.

289
00:18:01.810 --> 00:18:04.870
So let's see if we can find some hard-coded data here.

290
00:18:06.570 --> 00:18:10.690
We're going to click ListVehicles. There we go. This looks like the culprit.

291
00:18:10.850 --> 00:18:11.990
Let's delete this.

292
00:18:13.070 --> 00:18:17.210
And now we need a way to pull this data into our UI extension.

293
00:18:17.370 --> 00:18:18.290
And to do this,

294
00:18:19.110 --> 00:18:23.710
we're going to create a generated SDK that gives us a well-typed way to

295
00:18:23.810 --> 00:18:25.050
engage with those APIs.

296
00:18:25.130 --> 00:18:28.510
Just like you can use the Stripe SDK to call into Stripe's APIs,

297
00:18:28.710 --> 00:18:32.310
we're going to get the same thing, but for our vehicle and rental types.

298
00:18:32.310 --> 00:18:34.850
To do that, we're going to run stripe generate

299
00:18:35.450 --> 00:18:37.930
custom-objects-sdk.

300
00:18:38.510 --> 00:18:42.130
And we have to ask for this in a particular programming language.

301
00:18:42.210 --> 00:18:45.070
You could generate this in a bunch of the languages we support,

302
00:18:45.730 --> 00:18:50.370
but we're going to say, "Give me a node SDK."
Now,

303
00:18:50.850 --> 00:18:55.810
this is a good time to mention that Stripe Apps are now TypeScript monorepos.

304
00:18:55.870 --> 00:18:58.570
They contain a bunch of independent packages.

305
00:18:58.630 --> 00:19:02.970
So your UI extensions are one package, your custom objects are another package.

306
00:19:03.530 --> 00:19:06.430
And because we've asked for a node SDK here,

307
00:19:06.910 --> 00:19:11.350
we're being invited to incorporate this into our Stripe app as

308
00:19:11.850 --> 00:19:12.670
another package.

309
00:19:12.670 --> 00:19:17.490
We could just as easily take this to a backend node integration and use it

310
00:19:17.510 --> 00:19:21.270
there. But in this case, we want this in our UI, so we're going to say yes,

311
00:19:22.010 --> 00:19:23.390
incorporate it into my app.

312
00:19:24.970 --> 00:19:27.150
And next we're going to be asked,

313
00:19:28.210 --> 00:19:32.250
are there any packages within the app that we want to bring this SDK into?

314
00:19:32.250 --> 00:19:36.670
So we'll say, yes, I want that in my UI extensions. Great.

315
00:19:36.890 --> 00:19:39.370
So all the dependencies should be wired up here. Now,

316
00:19:39.930 --> 00:19:43.890
we probably want some test data to make sure that we can see something

317
00:19:43.970 --> 00:19:45.010
interesting in our UI.

318
00:19:45.090 --> 00:19:49.070
So I have a little seed script here and behind the scenes,

319
00:19:49.210 --> 00:19:52.850
all this is doing is running the same kinds of Stripe CLI commands that we were

320
00:19:52.890 --> 00:19:56.630
running earlier, Stripe post and then passing some JSON to it.

321
00:19:57.050 --> 00:19:59.770
We're just creating a bunch of customers, rentals, and vehicles,

322
00:19:59.830 --> 00:20:04.650
so we have something more interesting to look at in the UI.
Now,

323
00:20:05.770 --> 00:20:07.790
going back to our React component here,

324
00:20:08.590 --> 00:20:11.550
it's time for us to make a call to this SDK. So we'll say

325
00:20:12.070 --> 00:20:17.070
customSdk.v2.extend.objects

326
00:20:19.490 --> 00:20:20.323
and look at that.

327
00:20:20.470 --> 00:20:24.530
vehicles and rentals pop up just similar to how you'd see a list of Stripe

328
00:20:24.850 --> 00:20:27.970
resource types here if you were using our regular Node SDK.

329
00:20:27.970 --> 00:20:30.870
So we'll say vehicles and we want a list of those.

330
00:20:31.950 --> 00:20:35.650
And this kicks off the list API call.

331
00:20:35.970 --> 00:20:40.830
And when it comes back, just as we saw before in that list response,

332
00:20:41.010 --> 00:20:44.530
there's that data field with the array of vehicles that'll be in there.

333
00:20:44.590 --> 00:20:49.530
So let's reach in and grab data and then

334
00:20:49.550 --> 00:20:52.570
we'll just say setVehicles(data).

335
00:20:54.510 --> 00:20:56.270
And if we look back at our app,

336
00:20:56.410 --> 00:21:00.330
we see instantly this looks a lot more interesting.

337
00:21:00.730 --> 00:21:02.990
We don't have the same ID used for everything.

338
00:21:03.050 --> 00:21:05.490
We have a bunch of real makes and models. Now,

339
00:21:05.690 --> 00:21:09.430
part of why I didn't have to deploy here is I'm running stripe apps start.

340
00:21:09.510 --> 00:21:14.510
It's another Stripe CLI command where our local version of this

341
00:21:14.670 --> 00:21:16.690
app is embedded in the Stripe Dashboard.

342
00:21:16.750 --> 00:21:21.490
So you get that nice iterative development experience there.
So

343
00:21:22.130 --> 00:21:25.690
the rentals, sorry, vehicles look great. Let's look at rentals.

344
00:21:25.690 --> 00:21:26.970
This is still hard coded.

345
00:21:27.390 --> 00:21:31.610
So we'll just make a very similar change to the rentals list,

346
00:21:32.210 --> 00:21:36.590
replace that with this, just auto-import this SDK,

347
00:21:37.890 --> 00:21:39.330
change vehicles to rentals,

348
00:21:40.810 --> 00:21:44.030
and setRentals instead of setVehicles,

349
00:21:45.110 --> 00:21:48.690
and I'll hit save and swipe over. And there we go.

350
00:21:49.050 --> 00:21:53.410
That looks a lot better. We have customer IDs, we have these vehicle IDs,

351
00:21:53.690 --> 00:21:55.650
which begin with object record.

352
00:21:58.230 --> 00:22:02.330
So there we are. So we went from hard-coded arrays to live data.

353
00:22:02.570 --> 00:22:06.530
We combined our new custom object APIs with these great,

354
00:22:06.950 --> 00:22:08.330
beautiful charting components,

355
00:22:08.610 --> 00:22:12.710
all of it within this new full-page viewport that Stripe apps can now take

356
00:22:12.730 --> 00:22:13.563
advantage of.

357
00:22:13.930 --> 00:22:18.570
And now it feels like Rocket Rides has its own first class experience right in

358
00:22:18.630 --> 00:22:19.590
the Stripe Dashboard.

359
00:22:27.590 --> 00:22:31.310
<v 0>So UI extensions and the new components that Stripe released this week give you</v>

360
00:22:31.410 --> 00:22:35.670
more power and flexibility to build the visual expression of your business

361
00:22:35.970 --> 00:22:37.550
directly in the Stripe Dashboard.

362
00:22:38.410 --> 00:22:42.350
So we've covered the data and UI pillars of Stripe's extensibility platform.

363
00:22:42.830 --> 00:22:45.030
The last thing to cover is business logic.

364
00:22:46.030 --> 00:22:48.550
Custom objects aren't just about modeling data.

365
00:22:48.850 --> 00:22:52.850
You can of course model behaviors as well with custom object actions.

366
00:22:53.610 --> 00:22:58.010
These actions give you the ability to express your business logic and code and

367
00:22:58.030 --> 00:23:01.930
then deploy that code into Stripe. Now, as we saw earlier,

368
00:23:02.130 --> 00:23:05.530
you can define your data model using interfaces and types,

369
00:23:06.310 --> 00:23:10.210
but let's zoom in on that class definition at the bottom.
Just like any class in

370
00:23:10.790 --> 00:23:14.370
TypeScript, you can define functions that encapsulate business logic.

371
00:23:14.990 --> 00:23:17.470
In and of itself, this isn't that interesting or exciting,

372
00:23:17.530 --> 00:23:20.830
and that's honestly the whole point. These are simple functions,

373
00:23:21.390 --> 00:23:25.290
but the real power comes from an additional decorator known as

374
00:23:25.450 --> 00:23:29.570
action. By annotating your function with the action decorator,

375
00:23:29.970 --> 00:23:34.350
Stripe provisions one more API endpoint in addition to the CRUD APIs.

376
00:23:34.850 --> 00:23:39.210
And when you call it, the code in this function will be invoked. Now,

377
00:23:39.270 --> 00:23:41.070
as we've seen throughout the course of this talk,

378
00:23:41.730 --> 00:23:45.590
the power of these features is amplified when you use them together.

379
00:23:46.690 --> 00:23:47.650
Mike, I have some bad news.

380
00:23:48.170 --> 00:23:51.170
I was chatting with our finance team and we're taking some pretty big losses

381
00:23:51.210 --> 00:23:53.310
every time that a car comes back damaged.

382
00:23:54.030 --> 00:23:56.870
They are threatening to take away your second Claude code subscription if we

383
00:23:56.950 --> 00:23:57.830
can't fix this.

384
00:23:58.530 --> 00:23:59.430
<v 1>Whoa, whoa.</v>

385
00:23:59.610 --> 00:24:03.470
I appreciate artisanally handcrafted TypeScript code as much as the next

386
00:24:03.530 --> 00:24:05.450
engineer, but we probably should fix this.

387
00:24:06.570 --> 00:24:11.210
So here's the app that our employees in the rental return area use

388
00:24:11.730 --> 00:24:14.310
to assess damage on a car before we check it in.

389
00:24:14.370 --> 00:24:16.850
We got to figure out if there are repairs that are needed.

390
00:24:17.250 --> 00:24:20.990
So we can click on a car and then we can say,

391
00:24:20.990 --> 00:24:24.590
"the right mirror has a scratch," and we can hit save,

392
00:24:24.690 --> 00:24:27.110
but it looks like some things aren't working here yet.

393
00:24:27.750 --> 00:24:32.250
This to me looks like we're trying to create a damage report custom object

394
00:24:32.970 --> 00:24:36.370
or create a record of the damage report custom object,

395
00:24:36.650 --> 00:24:39.320
and this API doesn't exist. We're getting a 404.

396
00:24:40.170 --> 00:24:45.150
And if we were to go around and assess more damage on the car and then say,

397
00:24:45.150 --> 00:24:46.490
"All right, that's the extent of it.

398
00:24:47.110 --> 00:24:51.850
Let's say that this is done." We get another error message and we can see that

399
00:24:51.890 --> 00:24:56.590
there's a finalized action that we're attempting to call here and we're

400
00:24:56.630 --> 00:25:00.830
passing it a description of damage on the car. This doesn't exist yet either.

401
00:25:01.010 --> 00:25:05.790
So let's fix this.
We'll go back to our Stripe app and we're going to run

402
00:25:06.070 --> 00:25:07.530
stripe generate

403
00:25:10.430 --> 00:25:11.263
custom-object

404
00:25:13.090 --> 00:25:17.790
damage_report. And same thing as before,

405
00:25:17.950 --> 00:25:21.390
new scaffolding will happen, everything's wired up in the app manifest,

406
00:25:21.990 --> 00:25:26.790
and I have a damage report, custom object ready.

407
00:25:28.170 --> 00:25:30.410
So there it is.

408
00:25:30.670 --> 00:25:34.930
And then I have to copy a file over that we got from our finance team,

409
00:25:38.010 --> 00:25:40.770
put that in custom objects, source.

410
00:25:42.850 --> 00:25:43.683
Great.

411
00:25:43.930 --> 00:25:48.770
So what our finance team gave us was a fee calculation algorithm.

412
00:25:48.970 --> 00:25:51.850
So what you pass into it is a description of what's wrong with the car.

413
00:25:52.050 --> 00:25:55.750
It gives us some monetary amount back.

414
00:25:56.550 --> 00:25:59.130
And if we look at the fields on a damage report, we have a name,

415
00:25:59.250 --> 00:26:03.710
we have a state that the damage report can be in its either draft or finalized.

416
00:26:04.150 --> 00:26:06.910
We have a total fee that represents the damage on the car.

417
00:26:07.210 --> 00:26:12.050
Every damage report belongs to a rental and is associated with the driver.
Now,

418
00:26:12.330 --> 00:26:15.370
if we end up charging a driver for repairs,

419
00:26:15.890 --> 00:26:17.990
we end up creating an invoice line item.

420
00:26:17.990 --> 00:26:22.450
So we have a strong connection between this very rental-specific damage

421
00:26:22.510 --> 00:26:26.270
report concept and a built-in Stripe object that represents

422
00:26:27.230 --> 00:26:29.690
something that's associated with a payment. Now,

423
00:26:29.890 --> 00:26:33.150
the most interesting thing here, of course, is the action.

424
00:26:33.970 --> 00:26:35.770
And if we hover over this,

425
00:26:36.210 --> 00:26:40.930
we can see this is the API endpoint that Stripe will provision for us when we

426
00:26:41.070 --> 00:26:42.990
upload our next version of this app.

427
00:26:43.670 --> 00:26:48.650
So think of this as the argument passed to this

428
00:26:48.690 --> 00:26:49.523
TypeScript function,

429
00:26:49.810 --> 00:26:54.050
that's the request shape and whatever the return is from this function,

430
00:26:54.610 --> 00:26:58.510
that's the response shape.
Now, let's walk through what's happening here. First,

431
00:26:58.590 --> 00:27:01.890
we calculate the damage fee. We store it on the damage report.

432
00:27:02.330 --> 00:27:07.250
Then we're going to use the Stripe Node SDK to reach for the Stripe

433
00:27:07.330 --> 00:27:11.850
customer's API. We're going to go and grab that customer that to us is a driver.

434
00:27:12.750 --> 00:27:15.910
Now, we get a lot of business from corporate accounts.

435
00:27:15.910 --> 00:27:19.350
So if we find that this driver belongs to a corporate account,

436
00:27:19.410 --> 00:27:23.910
we give them 30% off. We go ahead and we create the invoice line item.

437
00:27:24.570 --> 00:27:28.650
We associate the line item with the damage report, set the status to finalized,

438
00:27:28.810 --> 00:27:31.070
and then down here we save.

439
00:27:31.510 --> 00:27:35.270
And what this will do is take all of those changes that we made to those fields

440
00:27:35.450 --> 00:27:40.110
and persist them using the custom object APIs.
So let's

441
00:27:40.450 --> 00:27:43.970
go ahead and upload this version of the app and make sure I save.

442
00:27:44.230 --> 00:27:47.350
And we're going to go stripe apps

443
00:27:47.690 --> 00:27:52.050
upload. And this will take care of

444
00:27:52.850 --> 00:27:56.790
creating a new version of an open API spec, provisioning that last endpoint.

445
00:27:56.910 --> 00:28:00.770
We're going to hit refresh over here just to make sure everything comes in.

446
00:28:01.570 --> 00:28:04.070
And if we go and click on a car here,

447
00:28:05.710 --> 00:28:10.610
we can start to assess damage like there's a scratch over here and then

448
00:28:11.030 --> 00:28:13.250
something happened with the experimental power source.

449
00:28:13.310 --> 00:28:15.610
There was an electrical overload of some kind,

450
00:28:16.070 --> 00:28:17.350
so we're going to have to cover that.

451
00:28:18.110 --> 00:28:21.410
And then we can say we're done and no more API errors.

452
00:28:21.470 --> 00:28:23.470
And if we look at the assessed vehicle list,

453
00:28:23.750 --> 00:28:28.590
there is the fee that came from our finance team's function and that

454
00:28:29.410 --> 00:28:33.670
now we have the business rule in place. So there it is,

455
00:28:33.830 --> 00:28:35.390
fee calculation, business rules,

456
00:28:35.610 --> 00:28:39.090
orchestrating over Stripe APIs like customers and invoices,

457
00:28:39.710 --> 00:28:44.310
all in a fully custom API that's built for and shaped just to meet

458
00:28:45.030 --> 00:28:47.470
Rocket Rides' needs. And it's all running on Stripe.

459
00:28:54.350 --> 00:28:57.070
<v 0>Mike gets to keep his second Claude code subscription after all.</v>

460
00:28:57.890 --> 00:29:01.750
So we started this talk by describing the first step of building a Stripe

461
00:29:01.770 --> 00:29:05.910
integration is etching the Rosetta Stone to translate between the language of

462
00:29:05.950 --> 00:29:09.190
your business and Stripe. With the data, UI,

463
00:29:09.690 --> 00:29:11.870
and logic features that we talked about today,

464
00:29:11.970 --> 00:29:15.610
you now have the ability to natively model your business on Stripe.

465
00:29:16.370 --> 00:29:19.830
With custom objects, you can extend Stripe with your own data model.

466
00:29:20.250 --> 00:29:24.630
You can articulate the language of your business in a fully featured programming

467
00:29:24.670 --> 00:29:26.570
language that you can run on Stripe.

468
00:29:27.290 --> 00:29:31.190
You can use those dynamically generated SDKs in seven different programming

469
00:29:31.230 --> 00:29:34.590
languages to interact with those objects, both in a Stripe app,

470
00:29:34.670 --> 00:29:37.750
but of course in your own codebase as well. And finally,

471
00:29:37.850 --> 00:29:39.350
with custom object actions,

472
00:29:39.450 --> 00:29:43.930
you can write and deploy code into Stripe that's invocable via an API.

473
00:29:44.850 --> 00:29:45.930
With UI extensions,

474
00:29:46.070 --> 00:29:50.710
you can build a custom UI directly within the Stripe Dashboard to optimize for

475
00:29:50.770 --> 00:29:52.530
your day-to-day workflows.

476
00:29:52.530 --> 00:29:55.550
There's a new full-page viewport that gives you the flexibility to tailor the

477
00:29:55.610 --> 00:29:57.870
Stripe Dashboard in exactly the way that you would like.

478
00:29:58.470 --> 00:30:00.350
And with our new data visualization components,

479
00:30:00.590 --> 00:30:03.870
you can build an overview page to see the health of your business at a glance.

480
00:30:04.990 --> 00:30:08.890
The extensibility features that we covered today reflect a core investment that

481
00:30:08.950 --> 00:30:13.370
Stripe is making in building a flexible and programmable platform.

482
00:30:13.790 --> 00:30:16.070
Whether it's a car rental business, an insurance company,

483
00:30:16.170 --> 00:30:17.710
or something completely different,

484
00:30:18.250 --> 00:30:20.690
you can now natively model your business on Stripe.

485
00:30:21.300 --> 00:30:23.900
If you're interested in learning more or signing up for these previews,

486
00:30:24.000 --> 00:30:28.960
please sign up at docs.stripe.com/extensibility or use that QR code there.

487
00:30:29.820 --> 00:30:30.460
Thank you all so much.

