The Marqeta Core API puts the power to build payment card programs in the hands of developers. The RESTful API uses standard POST, PUT, and GET methods to operate on resources. Regardless of the language, you use to build your Marqeta-backed application, you simply need to send HTTP requests to operate on the Core API.
Core API SDKs exist for Python and Ruby, but not for JavaScript. However, working with the API inside of a Node.js application is straightforward.
This article is a “Getting Started” guide for Node.js developers building on the platform. We’ll provide a walkthrough of common first-time-use endpoints. If you’re new to Marqeta, then this is your chance to get your feet wet and begin playing with the Core API from within a Node.js application. (By the way, keep in mind that we are not talking about Marqeta.js, which is “a JavaScript library used for displaying sensitive card data in your application or webpage.”)
Are you ready? Here we go!
All of the code that we’ll use in this walkthrough can be found in this public GitHub repository. Let’s walk through our steps to get set up.
First, you’ll need to create a Marqeta developer account. This will give you free access to use the developer sandbox and begin sending requests to the Core API. After you create your account and sign in, you’ll see the credentials that you will need for authentication.
We’ll need these token values and the Base URL soon.
On our local machine, let’s create a new folder and initialize a Node.js project with yarn (or npm), accepting all of the defaults.
~$ mkdir marqeta-node
~$ cd marqeta-node
~/marqeta-node$ yarn init
…
success Saved package.json
Done in 2.19s.
Our application has two dependencies: axios and dotenv. Let’s add them.
~/marqeta-node$ yarn add axios dotenv
…
Done in 0.68s.
APPLICATION_TOKEN=enter-your-token-here
ADMIN_ACCESS_TOKEN=enter-your-token-here
Make sure your .env file is ignored by your version control system.
We’ll be using axios to send all of our requests. Because all our requests will have some similarities, it makes sense to factor out these commonalities into a reusable module. Create a new file called marqeta-axios.js. The code will look like this:
// FILE: marqeta-axios.js
require('dotenv').config();
const { APPLICATION_TOKEN, ADMIN_ACCESS_TOKEN } = process.env
const axios = require('axios');
const authString = Buffer.
from(`${APPLICATION_TOKEN}:${ADMIN_ACCESS_TOKEN}`).
toString('base64');
axios.defaults.headers.common['Authorization'] = `Basic ${authString}`;
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.put['Content-Type'] = 'application/json';
axios.defaults.baseURL = 'https://sandbox-api.marqeta.com/v3';
const send = async (args = {}) => {
const method = args.method || 'GET';
try {
const options = {
method,
url: args.endpoint
}
if (args.data) {
options.data = args.data
}
const result = await axios(options);
return result.data;
} catch (e) {
console.log(e);
}
}
module.exports = send;
Briefly, this is what our module does:
Now that we’ve extracted axios usage to a module, we’re ready to make some API calls!
In your project folder, open a new file called index.js. At this point, your project folder should look like this:
~/marqeta-node$ tree -aL 1
.
├── .env
├── index.js
├── marqeta-axios.js
├── node_modules
├── package.json
└── yarn.lock
1 directory, 5 files
Let’s start with a basic call to list all users. To do this, we would ordinarily send a GET request to https://sandbox-api.marqeta.com/v3/users. However, we’ll be taking advantage of the send function from our marqeta-axios.js module. In that function, the default method is already specified as GET, and the base URL for our sandbox is already set, too. All we really need to do is:
let result = await send({ endpoint: '/users' });
That’s simple! Since this endpoint is for retrieving a list, the result has a sub-object called data, which is an array. Our entire file (so far) should look like this:
// FILE: index.js
const send = require('./marqeta-axios.js');
(async () => {
let result = await send({ endpoint: '/users' });
const users = result.data;
console.log(users);
})();
Because we use async/await with axios (and with our exported send function), we wrap the main code of index.js in an async function call, and we call that function immediately.
When we run node index.js at the command line, the result may look like this:
~/marqeta-node$ node index.js
[]
If you have a completely new Marqeta developer account, you won’t have any users yet, which is why the result is empty. If you’d like, you can use the Core API Explorer to create a few initial users.
As an alternative, you can skip down to the part of this walkthrough where we send a request to create a new user. Then, you can send this request to GET /users in your Node.js application again to see the new results.
For the remainder of our requests, we’ll assume that your account already has some initial data in it.
Next, let’s choose a user from the returned list, and we’ll list all cards for a user. The endpoint for this GET request is /cards/user/{token}. Let’s update our index.js file by selecting the first user returned and then generating the endpoint to get that user’s cards:
/ FILE: index.js
const send = require('./marqeta-axios.js');
(async () => {
let result = await send({ endpoint: '/users' });
const users = result.data;
const user = users[0];
console.log('USER', user);
result = await send({ endpoint: `/cards/user/${user.token}`});
const cards = result.data;
console.log('CARDS', cards);
})();
When we run node index.js, we see our first user and the cards for that user:
USER {
token: 'test-user-01',
active: true,
first_name: 'John',
last_name: 'Doe',
email: 'johndoe@gmailx.com',
uses_parent_account: false,
corporate_card_holder: false,
created_time: '2021-12-20T16:45:51Z',
last_modified_time: '2021-12-20T16:45:52Z',
metadata: {},
account_holder_group_token: 'DEFAULT_AHG',
status: 'ACTIVE'
}
CARDS [
{
created_time: '2021-12-20T20:37:46Z',
last_modified_time: '2021-12-20T20:37:46Z',
token: 'card-01',
user_token: 'test-user-01',
card_product_token: 'card-product-01',
last_four: '2517',
pan: '111111______2517',
expiration: '0126',
expiration_time: '2025-12-31T23:59:59Z',
barcode: '28295063411091980770',
pin_is_set: false,
state: 'ACTIVE',
state_reason: 'New card activated',
fulfillment_status: 'ISSUED',
instrument_type: 'PHYSICAL_MSR',
expedite: false,
metadata: {}
}
]
A card product resource represents the behavior and functionality of one or more cards. Every card is associated with a card product. When multiple cards all derive from the same card product, those cards will have common features (like their funding source or fulfillment specifications).
We can list all card products in our program by sending a GET request to /cardproducts. We add the following lines within our main function in index.js:
result = await send({ endpoint: '/cardproducts' });
const cardProducts = result.data;
console.log('CARD PRODUCTS', cardProducts);
If you have existing card products, then your result may look something like this:
...
CARD PRODUCTS [
{
token: 'card-product-01',
name: 'My card product 01',
active: true,
start_date: '2021-12-01',
config: {
poi: [Object],
transaction_controls: [Object],
selective_auth: [Object],
special: [Object],
card_life_cycle: [Object],
clearing_and_settlement: [Object],
jit_funding: [Object],
digital_wallet_tokenization: [Object],
fulfillment: [Object]
},
created_time: '2021-12-19T20:31:11Z',
last_modified_time: '2021-12-19T20:36:44Z'
}
]
So far, we’ve demonstrated some basic GET requests for retrieving a list of resources. Let’s expand on this.
Instead of getting a list of card products, we might want to retrieve a single card product based on its token. The endpoint for this GET request would be /cardproducts/{token}. In our JavaScript application, we would add this call:
const cardProductToken = cardProducts[0].token;
result = await send({ endpoint: `/cardproducts/${cardProductToken}` });
const cardProduct = result;
console.log('CARD PRODUCT [0]', cardProduct);
Because we are retrieving a single card product (instead of a list), notice that the card product is found in result (and not result.data, which is used for arrays).
The Core API supports field filtering, allowing you to specify which fields you want to return with your query. For example, let’s say we wanted to list all users, but we only wanted the tokens and first names for our users returned. Our endpoint would be: /users?fields=token,first_name.
result = await send({ endpoint: '/users?fields=token,first_name' });
console.log(result.data);
The result would look like this:
[
{ token: 'test-user-01', first_name: 'John' },
{ token: 'test-user-02', first_name: 'Jane' }
]
Most endpoints in the Core API support sorting. We add a sort_by query parameter set equal to the name of the field by which we want to support. We can prepend a - to the field name if we want reverse ordering. Note that the Marqeta documentation clarifies that “You must sort using system field names such as lastModifiedTime and createdTime, and not by the field names appearing in response bodies such as last_modified_time or created_time.”
For example, the following code would sort by ascending lastModifiedTime:
result = await send({ endpoint: '/users?sort_by=lastModifiedTime' });
To reverse the sort, we would add a - like this:
result = await send({ endpoint: '/users?sort_by=-lastModifiedTime' });
When requesting list endpoints, the result includes the array of resources in the data sub-object. However, the values for count, start_index, end_index, and is_more are also included.
When is_more is true, there are additional resources that exist for this query that are not included in the response. You can apply basic pagination techniques to set query parameters for count, start_index, and end_index to retrieve lists of resources in pages.
Creating resources uses POST requests, while updating objects uses PUT requests. Let’s cover creating resources first.
To create a new user, we send a POST request to /users, and we add a JSON request body. Let’s add this example to our index.js file:
result = await send({
endpoint: '/users',
method: 'POST',
data: {
token: 'test-user-03',
first_name: 'Ned',
last_name: 'Leeds'
}
});
console.log(result);
When we run this code, the result looks like this:
{
token: 'test-user-03',
active: true,
first_name: 'Ned',
last_name: 'Leeds',
uses_parent_account: false,
corporate_card_holder: false,
created_time: '2021-12-22T06:49:16Z',
last_modified_time: '2021-12-22T06:49:16Z',
metadata: {},
account_holder_group_token: 'DEFAULT_AHG',
status: 'ACTIVE',
deposit_account: {
token: '3f2fdf5c-105f-4bcf-86c4-5fd497642d1a',
account_number: '40011903013363369',
routing_number: '293748000',
allow_immediate_credit: false
}
}
Note that tokens are unique. You can specify the token for the user you are about to create, but you will only be able to make that request (with that token) one time. If you do not provide a token upon resource creation, Marqeta will generate one for you.
To create a program funding source for Managed Just-in-Time (JIT) Funding transactions, send a POST request to /fundingsources/program. Our JavaScript code would look like this:
result = await send({
endpoint: '/fundingsources/program',
method: 'POST',
data: {
token:'funding-source-01',
name:'My Program Funding Source',
active: true
}
});
console.log(result);
The result is:
{
name: 'My Program Funding Source',
active: true,
token: 'funding-source-01',
created_time: '2021-12-22T06:57:58Z',
last_modified_time: '2021-12-22T06:57:58Z',
account: '12.003.001.000000'
}
We can create a card product by sending a POST request to /cardproducts. We’ll create a card product that uses the funding source that we created above.
To simplify our creation, we’ll configure this card product to have a fulfillment.payment_instrument set to VIRTUAL_PAN. If we were to use the default value (PHYSICAL_MSR), we would then also need to specify a shipping address where physical cards would be delivered as a part of fulfillment.
Our JavaScript code would look like this:
result = await send({
endpoint: '/cardproducts',
method: 'POST',
data: {
token:'card-product-01',
name:'My Card Product',
active: true,
start_date: '2021-12-01',
config: {
card_life_cycle: {
activate_upon_issue: true
},
fulfillment: {
payment_instrument: 'VIRTUAL_PAN'
},
jit_funding: {
program_funding_source: {
funding_source_token: 'funding-source-01',
enabled: true
}
}
}
}
});
console.log(result);
With a user and a card product (associated with a funding source), we can now create a card for the user. To do this, we send a POST request to /cards, including the tokens for the user and the card product:
result = await send({
endpoint: '/cards',
method: 'POST',
data: {
token:'test-user-03-card-01',
user_token:'test-user-03',
card_product_token:'card-product-01'
}
});
console.log(result);
When updating objects, we send a PUT request to that resource’s endpoint, along with a data payload containing only those fields (and values) to be updated. Note that the token for a resource cannot be changed. For example, to update a user, our code would look like this:
result = await send({
endpoint: '/users/test-user-03',
method: 'PUT',
data: {
first_name: 'Edward',
middle_name: 'Ned',
state: 'New York'
}
});
console.log(result);
In a production environment, merchants initiate financial transactions upon sale. Within the developer sandbox, we can simulate transactions for testing purposes. To simulate an authorization type transaction, send a POST request to /simulate/authorization, making sure to include the token of the card on which the authorization is being made. The transaction amount and the merchant id (mid) are also required.
Our JavaScript code for simulating a transaction would look like this:
result = await send({
endpoint: '/simulate/authorization',
method: 'POST',
data: {
card_token: 'test-user-03-card-01',
amount: 49.99,
mid: 'merchant-01'
}
});
console.log(result);
The response contains a wealth of data about the transaction:
{
transaction: {
type: 'authorization',
state: 'PENDING',
identifier: '61',
token: '207befdf-9243-49ca-9274-9ff2c1cd8038',
user_token: 'test-user-03',
acting_user_token: 'test-user-03',
card_token: 'test-user-03-card-01',
gpa: {
currency_code: 'USD',
ledger_balance: 49.99,
available_balance: 0,
credit_balance: 0,
pending_credits: 0,
impacted_amount: -49.99,
balances: [Object]
},
gpa_order: {
token: 'd48d34ef-3363-4a6f-8439-eb6e4961544d',
amount: 49.99,
state: 'PENDING',
…
},
response: { code: '0000', memo: 'Approved or completed successfully' },
network: 'DISCOVER',
card: { last_four: '6730', metadata: {} },
…
},
…
}
Related to transactions, we can also retrieve the general purpose account (GPA) balance for any given user or business. To do so, we send a GET request to /balances/{token}.
result = await send({ endpoint: '/balances/test-user-03' });
console.log(result);
With our recent (simulated) transaction for this user, we see the result of this balance inquiry meets our expectations:
{
gpa: {
currency_code: 'USD',
ledger_balance: 49.99,
available_balance: 0,
…
},
…
}
That’s it! You now have a starting foundation for using JavaScript to work with the Marqeta Core API. The final version of index.js, with all of our requests, can be in the GitHub repository.
JavaScript developers building applications on the Marqeta platform can send requests to the Core API by using HTTP clients like axios or node-fetch. As we demonstrated above, commonalities among requests can easily be factored out to make up reusable modules.
Although there’s presently no JavaScript SDK for the Core API, we’ve seen how working with the Core API within a Node.js application is effortless. As you begin building your own Node.js + Marqeta application, be sure to seek out the Marqeta developer community for support. Happy coding!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.