How to: Issue cards

Use this how-to guide to see the flow for how to issue cards, enabilng your customers to spend money from a wallet.
*Card issuing is currently in a closed beta. Contact us for more information.

Moov enables you to instantly issue virtual cards on the Visa network, empowering a broad range of embedded spending and expense management solutions such as:

  • Replacing traditional checks for bill payments
  • Providing rapid access to funds from merchant processing
  • Facilitating immediate payments to vendors

Key players

For the purposes of this guide, we’ll assume you are a developer building a platform with the following players:

  • Your Moov account: Your platform that enables your customers to accept payments and use those funds for business-related purchases
  • Customer: Any business that will utilize the virtual card to make payments using funds from its Moov wallet

Prerequisites

This guide assumes that your customer’s Moov wallet has already been funded with a balance. This can be accomplished through:

  • A bank-to-wallet transfer
  • Funds that have accumulated in your wallet from accepting card payments

Read our wallets guide for more information on funding a wallet.

Your customer’s Moov wallet will be used as the funds source for the issued card.

Get your access token

Assuming you’ve already completed a basic setup of your own account, you’ll need to generate an access token. You will include a new access token as the Moov Drop onboarding.token whenever you onboard a customer.

1
2
3
4
curl -X POST "https://api.moov.io/oauth2/token" \
  -u "PUBLIC_KEY:PRIVATE_KEY" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode  "scope=/accounts.write" \
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { Moov, SCOPES } from '@moovio/node';

const moov = new Moov({
  accountID: "YOUR_MOOV_ACCOUNT_ID",
  publicKey: "PUBLIC_KEY",
  secretKey: "PRIVATE_KEY",
  domain: "YOUR_DOMAIN"
});

const scopes = [SCOPES.ACCOUNTS_CREATE];
try {
  const {token} = await moov.generateToken(scopes);
  // Do something with token
} catch(err) {
  // Handle any errors
}

Onboard your business customers

To issue cards using Moov, business customers must have their accounts enabled with both wallet and card-issuing capabilities.

Create the account

The following example uses the create account POST endpoint.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
curl -X POST "https://api.moov.io/accounts" \
  -H "Authorization: Bearer {token}" \
  --data-raw '{
    "accountType": "business",
    "profile": {
      "business": {
        "legalBusinessName": "Whole Body Fitness LLC",
        "businessType": "llc",
      }
    }
    "capabilities": [
      "wallet", 
      "card-issuing"
    ]
  }'\

Add representatives for the accounts

Input the details of key individuals who will manage the account. This includes personal and professional information critical for compliance and operational purposes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
curl -X POST "https://api.moov.io/accounts" \
  -H "Authorization: Bearer {token}" \
  --data-raw '{
    "accountType": "business",
    "profile": {
      "business": {
        "legalBusinessName": "Whole Body Fitness LLC",
        "businessType": "llc",
      }
    }
    "capabilities": [
      "wallet", 
      "card-issuing"
    ]
  }'\

Update ownersProvided

Depending on the type of business you are onboarding, you may need to update the ownersProvided field in the accounts PATCH endpoint.

Alternative onboarding methods

Alternatively, you can onboard using the Node SDK, Moov.js with your own UI, or Moov Drops (with a pre-built UI for onboarding).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// create Moov instance with generated token
const moov = new Moov(credentialsObject);

// create Moov account, likely on form submit
const accountPayload = {
  accountType: "business",
  profile: {
    business: {
      legalBusinessName: "Whole Body Fitness LLC",
      businessType: "llc",
      website: "wbfllc.com"
    }
  },
  capabilities: ["wallet", "card-issuing"]
};

moov.accounts.create(accountPayload).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
const accountID = "accountID";
const representativePayload = {
  name: {
    firstName: "George",
    lastName: "Glass"
  },
  address: {
    addressLine1: "12 Main Street",
    city: "Cabot Cove",
    stateOrProvince: "ME",
    postalCode: "04103",
    country: "US"
  },
  birthDate: {
    day: 10,
    month: 11,
    year: 1985
  },
  email: "georgeg@classbooker.dev",
  governmentID: {
    ssn: {
      full: "111111111",
      lastFour: "1111"
    }
  },
  responsibilities: {
    isController: false,
    isOwner: true,
    ownershipPercentage: 38,
    jobTitle: "CEO"
  }
}

moov.accounts.representatives.create(accountID, representativePayload);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// create Moov instance with generated token
const moov = Moov(token);

// create Moov account, likely on form submit
const accountPayload = {
  accountType: "business",
  profile: {
    business: {
      legalBusinessName: "Whole Body Fitness LLC",
      businessType: "llc",
      website: "wbfllc.com"
    }
  },
  capabilities: ["wallet", "card-issuing"]
};

moov.accounts.create({accountPayload}).then((account) => {
  console.log(account);
}).catch((err) => {
  console.error(err);
});

// Generate a new token server-side with the accountID and update the token within your frontend app. You can chain methods together
moov.setToken("new-token");

// add a representative to account
const accountID = "accountID";
const representativePayload = {
  name: {
    firstName: "George",
    lastName: "Glass"
  },
  address: {
    addressLine1: "12 Main Street",
    city: "Cabot Cove",
    stateOrProvince: "ME",
    postalCode: "04103",
    country: "US"
  },
  birthDate: {
    day: 10,
    month: 11,
    year: 1985
  },
  email: "georgeg@classbooker.dev",
  governmentID: {
    ssn: {
      full: "111111111",
      lastFour: "1111"
    }
  },
  responsibilities: {
    isController: false,
    isOwner: true,
    ownershipPercentage: 38,
    jobTitle: "CEO"
  }
};

moov.accounts.representatives.create(accountID, representativePayload);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Card issuing Moov Drop
const onboarding = document.querySelector("moov-onboarding");
// After generating a token, set it on the onboarding element
onboarding.token = "some-generated-token";

// Include your own accountID which can be found in the Moov Dashboard
onboarding.facilitatorAccountID = "your-account-id";

// Wallet and card issuing capabilities are needed for this flow
onboarding.capabilities = ["wallet", "card-issuing"];

// Funding will occur with the Moov wallet
onboarding.paymentMethodTypes = ["moov-wallet"];

// Open the onboarding flow when ready
onboarding.open = true;

Request a virtual card

After successfully onboarding and verifying your customer’s business, you can proceed to request a virtual card through the Moov API. This card can then be used by the customer for various business transactions.

Required parameters

To issue a virtual card, you will need to submit the following parameters:

Parameter Description
fundingWalletID The unique identifier for the Moov wallet associated with the customer account, ensuring the card is correctly linked to the funds source.
formFactor Currently, Moov supports only “virtual” cards. This parameter specifies the type of card being issued.
authorizedUser Moov requires the first and last names of the card user to comply with regulatory requirements. Optionally, include the user’s birthdate to enhance the accuracy of identity verification and reduce potential false positives during regulatory screenings.
expiration (optional) Set the card’s expiration date (1 to 24 months in the future). If not provided, it defaults to 24 months from the date of issuance.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
curl -X POST "https://api.moov.io/issuing/{accountID}/issued-cards" \
  -H "Authorization: Bearer {token}" \
  --data-raw '{
    "authorizedUser": {
      "firstName": "George",
      "lastName": "Glass",
      "birthDate": {
        "day": 29,
        "month": 4,
        "year": 1958
      }
    },
    "fundingWalletID": "string",
    "formFactor": "virtual",
    "memo": "supplies purchase",
    "expiration": {
        "month": "01",
        "year": "25"
    },
    "controls": {
      "singleUse": false,
      "velocityLimits" : [
        {
          "amount": 1000,
          "interval": "per-transaction"
        }
      ]
    }
  }'\

Once the virtual card has been issued, the customer can then use the card to make purchases within the amount of their wallet balance.

Authorized user screening

To comply with anti-money laundering (AML) regulations, Moov conducts regulatory screenings as part of the card issuing process. If the screening flags a potential match on regulatory watch lists, the card will enter the pending-verification state in the response from the initial create a spending card POST endpoint.

To address this, you can submit the authorizedUser.birthDate through the update a spending card PATCH endpoint if not initially provided, to help clear any discrepancies.

View and use card details

Once an issued card is in an active state, your customer can securely access the full PCI-compliant card details for making transactions through the Issued card Drop, a secure interface for displaying sensitive card information.

1
2
3
4
5
6
7
8
// Card issuing Moov Drop
const issuedCard = document.querySelector("moov-issued-card");
// After generating a token, set it on the issued card element
issuedCard.oauthToken = "some-generated-token";

// Include the accountID of the Moov account to which the card was issued and the ID of the issued card to display
issuedCard.accountID = "account-id";
issuedCard.issuedCardID = "card-id";

View issued card authorizations

Each time a card is used to make a purchase from a merchant, Moov processes the authorization request in real time. The outcomes of these requests can be tracked through the card authorization API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
curl -X GET "https://api.moov.io/issuing/{customer-account-ID}/authorizations" \
  -H "Authorization: Bearer {token}" \
  --data-raw '{
    "issuedCardID": "string",
    "startDateTime": "string",
    "endDateTime": "string",
    "statuses": 
      {"pending", "cleared"},
    "count": "string",
    "skip": "integer"
    }
  }'\

Below we show an example of the card authorization LIST response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[
  {
    "authorizationID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "authorizedAmount": "-14.89",
    "cardTransactions": [],
    "createdOn": "2019-08-24T14:15:22Z",
    "fundingWalletID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "issuedCardID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "merchantData": {
      "city": "San Francisco",
      "country": "US",
      "mcc": "7298",
      "name": "Whole Body Fitness",
      "networkID": "1234567890",
      "postalCode": "94107",
      "state": "CA"
    },
    "network": "visa",
    "status": "pending"
  }
]

View completed card transactions

When a pending authorization is successfully captured and funds have settled, a card transaction resource is created. This indicates that the funds related to that transaction have been moved. We retain details from the original authorization for reconciliation and tracking purposes.

You can list all card transactions associated with a particular Moov account by using the list card transactions GET endpoint as shown below:

1
2
3
4
5
6
curl -X GET "https://api.moov.io/issuing/{customer-account-ID}/authorizations" \
  -H "Authorization: Bearer {token}" \
  --data-raw '{
    "accountID": "string" 
    }
  }'\

Here’s an example of a completed card transaction:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[
  {
    "amount": "-14.89",
    "authorizationID": "f5f47bfa-fa5a-41f4-99eb-8671c1875b3f",
    "authorizedOn": "2019-08-24T14:15:22Z",
    "cardTransactionID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "createdOn": "2019-08-24T14:15:22Z",
    "fundingWalletID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "issuedCardID": "ec7e1848-dc80-4ab0-8827-dd7fc0737b43",
    "merchantData": {
      "city": "San Francisco",
      "country": "US",
      "mcc": "7298",
      "name": "Whole Body Fitness",
      "networkID": "1234567890",
      "postalCode": "94107",
      "state": "CA"
    }
  }
]
Summary Beta