Purchase the user's artwork
When a user purchases a print of their artwork, a partner must purchase the print-quality version of the artwork from Canva. The partner can then print the artwork and send it to the customer.

Step 1: Get the required order details

When a user purchases a print of their artwork, a partner must provide Canva with details about the order, such as the price of each item in the order. Canva uses this data to verify the revenue a partner owes. You can find the list of required properties in the API reference.
For this tutorial, pass placeholder amounts into the "cart" view:
1
app.get("/cart", async (request, response) => {
2
response.render("cart", {
3
artworkId: request.query.artworkId,
4
artworkTitle: request.query.artworkTitle,
5
previewImageSrc: request.query.previewImageSrc,
6
order: "OR_12345672",
7
item: "IT_12345672-003",
8
sku: "PS034509",
9
quantity: 5,
10
currency: "USD",
11
grossAmount: 65.5,
12
discountAmount: 6.55,
13
netAmount: 58.95,
14
});
15
});
Copied!
Then render these values in the view:
1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta
6
name="viewport"
7
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
8
/>
9
<title>Shopping cart</title>
10
</head>
11
<body>
12
<h1>Shopping cart</h1>
13
<h2>Order #<%= order %></h2>
14
<table>
15
<thead>
16
<tr>
17
<td>Item ID</td>
18
<td>Product SKU</td>
19
<td>Artwork ID</td>
20
<td>Artwork title</td>
21
<td>Preview</td>
22
<td>Quantity</td>
23
<td>Price (per item)</td>
24
</tr>
25
</thead>
26
<tbody>
27
<tr>
28
<td><%= item %></td>
29
<td><%= sku %></td>
30
<td><%= artworkId %></td>
31
<td><%= artworkTitle %></td>
32
<td><a href="<%= previewImageSrc %>">Click here</a></td>
33
<td><%= quantity %></td>
34
<td>lt;%= grossAmount %> <%= currency %></td>
35
</tr>
36
</tbody>
37
</table>
38
<p>Price (with discounts): lt;%= netAmount * quantity %> <%= currency %></p>
39
</body>
40
</html>
Copied!

Step 2: Create an order form

To simulate the ordering of a print, add a form to the cart.ejs file:
1
<form method="POST" action="/process-order">
2
<input type="hidden" name="artworkId" value="<%= artworkId %>" />
3
<input type="hidden" name="currency" value="<%= currency %>" />
4
<input type="hidden" name="discountAmount" value="<%= discountAmount %>" />
5
<input type="hidden" name="grossAmount" value="<%= grossAmount %>" />
6
<input type="hidden" name="item" value="<%= item %>" />
7
<input type="hidden" name="netAmount" value="<%= netAmount %>" />
8
<input type="hidden" name="order" value="<%= order %>" />
9
<input type="hidden" name="quantity" value="<%= quantity %>" />
10
<input type="hidden" name="sku" value="<%= sku %>" />
11
<button>Confirm order</button>
12
</form>
Copied!
When a user submits this form, it sends a POST request to /process-order, which is an endpoint that doesn't exist (yet). The hidden fields contain the data that Canva requires for processing orders.

Step 3: Purchase the artwork from Canva

In the server.js file, create a route that receives POST requests:
1
app.post("/process-order", async (request, response) => {
2
// code goes here
3
});
Copied!
In this route, send a POST request to the following endpoint:
1
https://api.canva.com/_tpi/partnership/<partner_id>/artworks/<artwork_id>
Copied!
In this request:
    Replace <partner_id> with the Partner ID.
    Replace <artwork_id> with the ID of the user's artwork.
    Set the Authorization header to the Artwork API secret.
    Include a purchaseConfirmation object in the body. The object must contain the data that Canva requires to process the order, the SKU of each product.
An integration must send a separate request for each unique item in an order. This means a single order may require multiple requests.
For security reasons, you must send this request from a backend server. If you send this request via a browser, Canva rejects the request.
This example destructures the required data from the request body and uses the axios library to send a request:
1
app.post("/process-order", async (request, response) => {
2
// Get the required data from the incoming request body
3
const {
4
artworkId,
5
currency,
6
discountAmount,
7
grossAmount,
8
item,
9
netAmount,
10
order,
11
quantity,
12
sku,
13
} = request.body;
14
15
// Send a request to Canva
16
const { data } = await axios.request({
17
baseURL: "https://api.canva.com",
18
url: `/_tpi/partnership/${process.env.PARTNER_ID}/artworks/${artworkId}`,
19
method: "post",
20
headers: {
21
Authorization: process.env.ARTWORK_API_SECRET,
22
},
23
data: {
24
purchaseConfirmation: {
25
currency,
26
discountAmount: parseFloat(discountAmount),
27
grossAmount: parseFloat(grossAmount),
28
item,
29
netAmount: parseFloat(netAmount),
30
order,
31
quantity: parseInt(quantity),
32
sku,
33
},
34
},
35
});
36
37
// Log the results
38
console.log(data);
39
});
Copied!
The response body contains a productionFile property that contains the URL of the print-quality artwork.
Canva charges partners for each purchase made in a production environment. To avoid being charged while testing an integration, use the test credentials.
For the complete API reference, refer to Purchase an artwork.

Step 4: Print the user's artwork

A real integration should download the print-quality version of the user's artwork and send it off for printing. For this tutorial, simply redirect users to the print-quality artwork:
1
// Redirect users to the print-quality artwork
2
response.redirect(data.productionFile);
Copied!

Example

src/views/cart.ejs

1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta
6
name="viewport"
7
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
8
/>
9
<title>Shopping cart</title>
10
</head>
11
<body>
12
<h1>Shopping cart</h1>
13
<h2>Order #<%= order %></h2>
14
<table>
15
<thead>
16
<tr>
17
<td>Item ID</td>
18
<td>Product SKU</td>
19
<td>Artwork ID</td>
20
<td>Artwork title</td>
21
<td>Preview</td>
22
<td>Quantity</td>
23
<td>Price (per item)</td>
24
</tr>
25
</thead>
26
<tbody>
27
<tr>
28
<td><%= item %></td>
29
<td><%= sku %></td>
30
<td><%= artworkId %></td>
31
<td><%= artworkTitle %></td>
32
<td><a href="<%= previewImageSrc %>">Click here</a></td>
33
<td><%= quantity %></td>
34
<td>lt;%= grossAmount %> <%= currency %></td>
35
</tr>
36
</tbody>
37
</table>
38
39
<p>Price (with discounts): lt;%= netAmount * quantity %> <%= currency %></p>
40
41
<form method="POST" action="/process-order">
42
<input type="hidden" name="artworkId" value="<%= artworkId %>" />
43
<input type="hidden" name="currency" value="<%= currency %>" />
44
<input
45
type="hidden"
46
name="discountAmount"
47
value="<%= discountAmount %>"
48
/>
49
<input type="hidden" name="grossAmount" value="<%= grossAmount %>" />
50
<input type="hidden" name="item" value="<%= item %>" />
51
<input type="hidden" name="netAmount" value="<%= netAmount %>" />
52
<input type="hidden" name="order" value="<%= order %>" />
53
<input type="hidden" name="quantity" value="<%= quantity %>" />
54
<input type="hidden" name="sku" value="<%= sku %>" />
55
<button>Confirm order</button>
56
</form>
57
</body>
58
</html>
Copied!

src/views/index.ejs

1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta
6
name="viewport"
7
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
8
/>
9
<title>Print Partnership Example</title>
10
<script src="https://sdk.canva.com/partnership.js"></script>
11
<style type="text/css">
12
body,
13
html {
14
margin: 0;
15
}
16
17
#container {
18
height: 100vh;
19
}
20
</style>
21
</head>
22
<body>
23
<div id="container"></div>
24
<script type="text/javascript">
25
(async () => {
26
const api = await Canva.Partnership.initialize({
27
apiKey: "<%= partnerApiKey %>",
28
container: document.getElementById("container"),
29
autoAuthToken: "<%= autoAuthToken %>",
30
});
31
32
const onBackClick = () => {
33
console.log(
34
"You clicked the 'Back' button in the Canva editor's header."
35
);
36
};
37
38
const onArtworkCreate = (opts) => {
39
// Create a query string
40
const params = new URLSearchParams();
41
42
// Add opts (artworkId, etc) to query string
43
Object.keys(opts).forEach((key) => {
44
params.append(key, opts[key]);
45
});
46
47
// Redirect the user to shopping cart
48
window.location.href = `/cart?${params.toString()}`;
49
};
50
51
const onProductSelect = (opts) => {
52
api.createDesign({
53
...opts,
54
onBackClick,
55
onArtworkCreate,
56
});
57
};
58
59
api.showCatalog({
60
onProductSelect,
61
});
62
})();
63
</script>
64
</body>
65
</html>
Copied!

src/server.js

1
require("dotenv").config();
2
const axios = require("axios");
3
const express = require("express");
4
const jwt = require("jwt-simple");
5
6
const app = express();
7
8
app.set("views", "./src/views");
9
app.set("view engine", "ejs");
10
11
app.use(express.json());
12
app.use(express.urlencoded({ extended: true }));
13
14
app.get("/", async (request, response) => {
15
// Get the current time
16
const now = Math.floor(new Date().getTime() / 1000);
17
18
// Create a payload
19
const payload = {
20
iss: process.env.PARTNER_API_KEY,
21
sub: "12345",
22
iat: now,
23
exp: now + 60 * 30,
24
};
25
26
// Calculate the autoAuthToken
27
const autoAuthToken = jwt.encode(payload, process.env.PARTNER_API_SECRET);
28
29
response.render("index", {
30
autoAuthToken,
31
partnerApiKey: process.env.PARTNER_API_KEY,
32
});
33
});
34
35
app.get("/cart", async (request, response) => {
36
response.render("cart", {
37
artworkId: request.query.artworkId,
38
artworkTitle: request.query.artworkTitle,
39
previewImageSrc: request.query.previewImageSrc,
40
order: "OR_12345672",
41
item: "IT_12345672-003",
42
sku: "PS034509",
43
quantity: 5,
44
currency: "USD",
45
grossAmount: 65.5,
46
discountAmount: 6.55,
47
netAmount: 58.95,
48
});
49
});
50
51
app.post("/process-order", async (request, response) => {
52
// Get the required data from the incoming request body
53
const {
54
artworkId,
55
currency,
56
discountAmount,
57
grossAmount,
58
item,
59
netAmount,
60
order,
61
quantity,
62
sku,
63
} = request.body;
64
65
// Send a request to Canva
66
const { data } = await axios.request({
67
baseURL: "https://api.canva.com",
68
url: `/_tpi/partnership/${process.env.PARTNER_ID}/artworks/${artworkId}`,
69
method: "post",
70
headers: {
71
Authorization: process.env.ARTWORK_API_SECRET,
72
},
73
data: {
74
purchaseConfirmation: {
75
currency,
76
discountAmount: parseFloat(discountAmount),
77
grossAmount: parseFloat(grossAmount),
78
item,
79
netAmount: parseFloat(netAmount),
80
order,
81
quantity: parseInt(quantity),
82
sku,
83
},
84
},
85
});
86
87
// Redirect users to the print-quality artwork
88
response.redirect(data.productionFile);
89
});
90
91
app.listen(process.env.PORT || 3000);
Copied!
Last modified 3mo ago