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.

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:

app.get("/cart", async (request, response) => {
response.render("cart", {
artworkId: request.query.artworkId,
artworkTitle: request.query.artworkTitle,
previewImageSrc: request.query.previewImageSrc,
order: "OR_12345672",
item: "IT_12345672-003",
sku: "PS034509",
quantity: 5,
currency: "USD",
grossAmount: 65.5,
discountAmount: 6.55,
netAmount: 58.95,
});
});
javascript

Then render these values in the view:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
/>
<title>Shopping cart</title>
</head>
<body>
<h1>Shopping cart</h1>
<h2>Order #<%= order %></h2>
<table>
<thead>
<tr>
<td>Item ID</td>
<td>Product SKU</td>
<td>Artwork ID</td>
<td>Artwork title</td>
<td>Preview</td>
<td>Quantity</td>
<td>Price (per item)</td>
</tr>
</thead>
<tbody>
<tr>
<td><%= item %></td>
<td><%= sku %></td>
<td><%= artworkId %></td>
<td><%= artworkTitle %></td>
<td><a href="<%= previewImageSrc %>">Click here</a></td>
<td><%= quantity %></td>
<td>$<%= grossAmount %> <%= currency %></td>
</tr>
</tbody>
</table>
<p>Price (with discounts): $<%= netAmount * quantity %> <%= currency %></p>
</body>
</html>
html

To simulate the ordering of a print, add a form to the cart.ejs file:

<form method="POST" action="/process-order">
<input type="hidden" name="artworkId" value="<%= artworkId %>" />
<input type="hidden" name="currency" value="<%= currency %>" />
<input type="hidden" name="discountAmount" value="<%= discountAmount %>" />
<input type="hidden" name="grossAmount" value="<%= grossAmount %>" />
<input type="hidden" name="item" value="<%= item %>" />
<input type="hidden" name="netAmount" value="<%= netAmount %>" />
<input type="hidden" name="order" value="<%= order %>" />
<input type="hidden" name="quantity" value="<%= quantity %>" />
<input type="hidden" name="sku" value="<%= sku %>" />
<button>Confirm order</button>
</form>
html

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.

In the server.js file, create a route that receives POST requests:

app.post("/process-order", async (request, response) => {
// code goes here
});
javascript

In this route, send a POST request to the following endpoint:

https://api.canva.com/_tpi/partnership/<partner_id>/artworks/<artwork_id>
bash

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.

This example destructures the required data from the request body and uses the axios library to send a request:

app.post("/process-order", async (request, response) => {
// Get the required data from the incoming request body
const {
artworkId,
currency,
discountAmount,
grossAmount,
item,
netAmount,
order,
quantity,
sku,
} = request.body;
// Send a request to Canva
const { data } = await axios.request({
baseURL: "https://api.canva.com",
url: `/_tpi/partnership/${process.env.PARTNER_ID}/artworks/${artworkId}`,
method: "post",
headers: {
Authorization: process.env.ARTWORK_API_SECRET,
},
data: {
purchaseConfirmation: {
currency,
discountAmount: parseFloat(discountAmount),
grossAmount: parseFloat(grossAmount),
item,
netAmount: parseFloat(netAmount),
order,
quantity: parseInt(quantity),
sku,
},
},
});
// Log the results
console.log(data);
});
javascript

The response body contains a productionFile property that contains the URL of the print-quality 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:

// Redirect users to the print-quality artwork
response.redirect(data.productionFile);
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
/>
<title>Shopping cart</title>
</head>
<body>
<h1>Shopping cart</h1>
<h2>Order #<%= order %></h2>
<table>
<thead>
<tr>
<td>Item ID</td>
<td>Product SKU</td>
<td>Artwork ID</td>
<td>Artwork title</td>
<td>Preview</td>
<td>Quantity</td>
<td>Price (per item)</td>
</tr>
</thead>
<tbody>
<tr>
<td><%= item %></td>
<td><%= sku %></td>
<td><%= artworkId %></td>
<td><%= artworkTitle %></td>
<td><a href="<%= previewImageSrc %>">Click here</a></td>
<td><%= quantity %></td>
<td>$<%= grossAmount %> <%= currency %></td>
</tr>
</tbody>
</table>
<p>Price (with discounts): $<%= netAmount * quantity %> <%= currency %></p>
<form method="POST" action="/process-order">
<input type="hidden" name="artworkId" value="<%= artworkId %>" />
<input type="hidden" name="currency" value="<%= currency %>" />
<input
type="hidden"
name="discountAmount"
value="<%= discountAmount %>"
/>
<input type="hidden" name="grossAmount" value="<%= grossAmount %>" />
<input type="hidden" name="item" value="<%= item %>" />
<input type="hidden" name="netAmount" value="<%= netAmount %>" />
<input type="hidden" name="order" value="<%= order %>" />
<input type="hidden" name="quantity" value="<%= quantity %>" />
<input type="hidden" name="sku" value="<%= sku %>" />
<button>Confirm order</button>
</form>
</body>
</html>
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
/>
<title>Print Partnership Example</title>
<script src="https://sdk.canva.com/partnership.js"></script>
<style type="text/css">
body,
html {
margin: 0;
}
#container {
height: 100vh;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
(async () => {
const api = await Canva.Partnership.initialize({
apiKey: "<%= partnerApiKey %>",
container: document.getElementById("container"),
autoAuthToken: "<%= autoAuthToken %>",
});
const onBackClick = () => {
console.log(
"You clicked the 'Back' button in the Canva editor's header."
);
};
const onArtworkCreate = (opts) => {
// Create a query string
const params = new URLSearchParams();
// Add opts (artworkId, etc) to query string
Object.keys(opts).forEach((key) => {
params.append(key, opts[key]);
});
// Redirect the user to shopping cart
window.location.href = `/cart?${params.toString()}`;
};
const onProductSelect = (opts) => {
api.createDesign({
...opts,
onBackClick,
onArtworkCreate,
});
};
api.showCatalog({
onProductSelect,
});
})();
</script>
</body>
</html>
html
require("dotenv").config();
const axios = require("axios");
const express = require("express");
const jwt = require("jwt-simple");
const app = express();
app.set("views", "./src/views");
app.set("view engine", "ejs");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/", async (request, response) => {
// Get the current time
const now = Math.floor(new Date().getTime() / 1000);
// Create a payload
const payload = {
iss: process.env.PARTNER_API_KEY,
sub: "12345",
iat: now,
exp: now + 60 * 30,
};
// Calculate the autoAuthToken
const autoAuthToken = jwt.encode(payload, process.env.PARTNER_API_SECRET);
response.render("index", {
autoAuthToken,
partnerApiKey: process.env.PARTNER_API_KEY,
});
});
app.get("/cart", async (request, response) => {
response.render("cart", {
artworkId: request.query.artworkId,
artworkTitle: request.query.artworkTitle,
previewImageSrc: request.query.previewImageSrc,
order: "OR_12345672",
item: "IT_12345672-003",
sku: "PS034509",
quantity: 5,
currency: "USD",
grossAmount: 65.5,
discountAmount: 6.55,
netAmount: 58.95,
});
});
app.post("/process-order", async (request, response) => {
// Get the required data from the incoming request body
const {
artworkId,
currency,
discountAmount,
grossAmount,
item,
netAmount,
order,
quantity,
sku,
} = request.body;
// Send a request to Canva
const { data } = await axios.request({
baseURL: "https://api.canva.com",
url: `/_tpi/partnership/${process.env.PARTNER_ID}/artworks/${artworkId}`,
method: "post",
headers: {
Authorization: process.env.ARTWORK_API_SECRET,
},
data: {
purchaseConfirmation: {
currency,
discountAmount: parseFloat(discountAmount),
grossAmount: parseFloat(grossAmount),
item,
netAmount: parseFloat(netAmount),
order,
quantity: parseInt(quantity),
sku,
},
},
});
// Redirect users to the print-quality artwork
response.redirect(data.productionFile);
});
app.listen(process.env.PORT || 3000);
javascript