Official Swift SDK for MakePay server-side integrations and Swift apps that need MakePay API and checkout helpers. Use it to create crypto payment links, donation pages, invoices, bookkeeping records, subscriptions, POS terminals, products, Simple Shop storefronts, customer portals, branded domains, and signed webhook handlers.
Add the package to Package.swift:
dependencies: [
.package(url: "https://github.com/makecryptoio/makepay-swift-sdk.git", from: "0.3.2")
]Then add MakePay to your target dependencies:
.target(
name: "YourApp",
dependencies: ["MakePay"]
)The SDK targets Swift 5.9 or newer and uses async/await.
In Xcode, use File > Add Package Dependencies... and enter:
https://github.com/makecryptoio/makepay-swift-sdk.git
Select version 0.3.2 or the default "up to next major" rule from 0.3.2,
then add the MakePay library product to your app or server target.
- Swift 5.9 or newer.
- iOS 13+, macOS 10.15+, tvOS 13+, or watchOS 6+.
- A trusted backend or server-side Swift runtime for authenticated API calls.
MAKEPAY_KEY_IDandMAKEPAY_KEY_SECRETfor partner API requests.MAKEPAY_WEBHOOK_SECRETfor webhook signature verification.
Create a MakePay API key in MakeCrypto and keep the secret on trusted backend infrastructure only. Do not ship API key secrets inside iOS, macOS, tvOS, or watchOS apps.
import MakePay
let makepay = MakePayClient(
keyID: ProcessInfo.processInfo.environment["MAKEPAY_KEY_ID"]!,
keySecret: ProcessInfo.processInfo.environment["MAKEPAY_KEY_SECRET"]!
)The client sends X-MakeCrypto-Key-Id and X-MakeCrypto-Key-Secret headers to
the MakePay partner API.
Use the optional baseURL, checkoutBaseURL, and transport initializer
arguments for staging environments, custom checkout domains, or tests:
let makepay = MakePayClient(
keyID: keyID,
keySecret: keySecret,
baseURL: URL(string: "https://www.makecrypto.io")!,
checkoutBaseURL: URL(string: "https://makepay.io")!
)Create a payment link on your backend, save the returned payment UID or public URL with your order, then redirect the shopper to hosted checkout.
let response = try await makepay.createPaymentLink(
MakePayPaymentLinkPayload(
amount: "49.00",
currency: "USDT",
fiatCurrency: "USD",
title: "Order #1042",
orderID: "order_1042",
successURL: URL(string: "https://merchant.example/orders/1042/success"),
failureURL: URL(string: "https://merchant.example/orders/1042/pay")
),
sendPaymentRequestEmail: true
)
let paymentLink = response["paymentLink"]?.objectValue
let paymentUID = paymentLink?["uid"]?.stringValue
let checkoutURL = paymentUID.flatMap { try? makepay.hostedCheckoutURL(paymentUID: $0) }
let publicURL = paymentLink?["publicUrl"]?.stringValuelet response = try await makepay.createPaymentLink(
MakePayPaymentLinkPayload(
amount: "129.99",
currency: "USDT",
title: "Order #1042",
description: "Checkout for order #1042",
orderID: "order_1042",
customerEmail: "buyer@example.com",
returnURL: URL(string: "https://merchant.example/orders/1042"),
successURL: URL(string: "https://merchant.example/orders/1042/success"),
failureURL: URL(string: "https://merchant.example/orders/1042/pay"),
expirationTime: "12h"
)
)
let paymentLink = response["paymentLink"]?.objectValue
let publicURL = paymentLink?["publicUrl"]?.stringValueRead, update, and email existing links:
try await makepay.listPaymentLinks()
try await makepay.getPaymentLink("PAYMENT_LINK_UID")
try await makepay.updatePaymentLink("PAYMENT_LINK_UID", status: .paused)
try await makepay.sendPaymentRequestEmail("PAYMENT_LINK_UID", email: "buyer@example.com")Donation pages are flexible-amount payment links with a public donation slug.
let donation = try await makepay.createDonationLink(
MakePayDonationLinkPayload(
defaultAmountUSD: "25",
minimumAmountUSD: "5",
donationSlug: "spring-campaign",
title: "Spring campaign",
description: "Support the 2026 spring fundraiser."
)
)
let donationURL = try makepay.hostedDonationURL(donationSlug: "spring-campaign")
try await makepay.listDonationLinks()
try await makepay.getDonationLink("DONATION_UID")
try await makepay.updateDonationLink("DONATION_UID", status: .paused)Anonymous links do not use a MakePay API key. They require an explicit settlement route because MakePay cannot read merchant wallet settings.
let response = try await createAnonymousPaymentLink([
"amount": "25",
"title": "Invoice #1042",
"webhookUrl": "https://merchant.example/webhooks/makepay",
"settlement": [
"currency": "USDT",
"priorities": [
[
"chain": "ETH",
"address": "0xYourSettlementWallet",
"asset": "ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7",
],
],
],
])MakePay does not create reusable static payment addresses for new orders. Migrate static-address flows to one-time or deposit payment links.
Use hosted checkout for redirects, or the embed helpers when your frontend keeps the shopper on the merchant site.
let paymentUID = "PAYMENT_LINK_UID"
let hostedURL = try makepay.hostedCheckoutURL(paymentUID: paymentUID)
let embeddedURL = try makepay.embeddedCheckoutURL(
paymentUID: paymentUID,
parentOrigin: URL(string: "https://merchant.example")
)
let modalScriptURL = makepay.modalScriptURL()
let buttonHTML = try makepay.embedButtonHTML(paymentUID: paymentUID)
let iframeHTML = try makepay.iframeHTML(paymentUID: paymentUID)Donation pages also have URL helpers:
try makepay.hostedDonationURL(donationSlug: "spring-campaign")
try makepay.embeddedDonationURL(
donationSlug: "spring-campaign",
parentOrigin: URL(string: "https://merchant.example")
)Advanced payloads use MakePayObject, which is a Swift alias for
[String: JSONValue].
try await makepay.upsertCustomer([
"email": "buyer@example.com",
"name": "Buyer Example",
"clientId": "crm_123",
])
try await makepay.createCustomerPortal(
customerID: "CUSTOMER_ID",
payload: ["returnUrl": "https://merchant.example/account"]
)
try await makepay.createSubscription([
"amountUsd": "29",
"customerEmail": "buyer@example.com",
"label": "Monthly plan",
"billingIntervalUnit": "month",
"billingIntervalCount": 1,
])let terminal = try await makepay.createPosTerminal(
MakePayPOSTerminalPayload(
name: "Front counter",
pin: "1234",
allowedAssets: ["ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7"],
emailCollectionMode: "optional_after_deposit",
catalogEnabled: true
)
)
try await makepay.listPosTerminals()
try await makepay.getPosTerminal("TERMINAL_UID")
try await makepay.updatePosTerminal("TERMINAL_UID", payload: ["status": "paused"])try await makepay.createProduct([
"name": "Digital guide",
"productType": "digital",
"basePriceUsd": "19",
"shopSlug": "digital-guide",
"images": [["url": "https://merchant.example/guide.png", "alt": "Guide cover"]],
"variants": [["name": "PDF", "priceUsd": "19"]],
])
try await makepay.createProductDownload(
productID: "PRODUCT_UID",
payload: [
"fileName": "guide.pdf",
"contentType": "application/pdf",
"url": "https://merchant.example/downloads/guide.pdf",
]
)
try await makepay.updateShop([
"slug": "merchant-shop",
"displayCurrency": "USD",
"checkoutMode": "hosted",
"branding": ["accentColor": "#14b8a6"],
])
try await makepay.updateShopDomain("shop.merchant.example")
try await makepay.refreshShopDomain()
try await makepay.createShopCoupon([
"code": "SPRING10",
"discountType": "percent",
"value": "10",
])
try await makepay.listShopOrders(query: ["status": "paid", "limit": "25"])Bookkeeping APIs manage merchant invoices, expenses, supporting documents, OCR, and reconciliation links.
let created = try await makepay.createBookkeepingInvoice(
MakePayBookkeepingInvoicePayload(
currency: "USD",
issueDate: "2026-05-15",
dueDate: "2026-05-30",
title: "Invoice #1042",
counterparty: MakePayBookkeepingCounterpartyPayload(
name: "Buyer Example",
email: "buyer@example.com",
clientID: "crm_123"
),
lineItems: [
MakePayBookkeepingInvoiceLineItemPayload(
description: "Implementation services",
quantity: "1",
unitAmount: "500",
taxAmount: "0"
),
],
metadata: ["orderId": "order_1042"]
)
)
try await makepay.createBookkeepingInvoicePaymentLink(
invoiceID: "INVOICE_UID",
options: ["sendPaymentRequestEmail": true]
)
try await makepay.listBookkeepingInvoices()
try await makepay.getBookkeepingInvoice("INVOICE_UID")
try await makepay.updateBookkeepingInvoice("INVOICE_UID", payload: ["status": "open"])Expenses can be created manually or from wallet activity, then linked back to payments, transfers, invoices, or uploaded receipts.
try await makepay.createBookkeepingExpense([
"title": "Hosting",
"amount": "49",
"currency": "USD",
"incurredOn": "2026-05-15",
"category": "Infrastructure",
"counterparty": ["name": "Vendor Example", "type": "vendor"],
])
try await makepay.createBookkeepingExpenseFromActivity([
"walletActivityEventKey": "CHAIN_EVENT_KEY",
"category": "Settlement",
])
try await makepay.createBookkeepingReconciliation(
MakePayBookkeepingReconciliationPayload(
invoiceID: "INVOICE_UID",
paymentSessionID: "PAYMENT_SESSION_ID",
linkType: "payment"
)
)Document uploads use multipart form data.
try await makepay.uploadBookkeepingDocument(
MakePayBookkeepingDocumentUpload(
file: MakePayMultipartFile(
data: receiptData,
fileName: "receipt.pdf",
contentType: "application/pdf"
),
documentType: "receipt",
expenseID: "EXPENSE_UID"
)
)
try await makepay.listBookkeepingDocuments()
try await makepay.getBookkeepingDocumentDownloadURL("DOCUMENT_UID")
try await makepay.runBookkeepingDocumentOCR("DOCUMENT_UID")
try await makepay.getBookkeepingSummary()try await makepay.updateBranding([
"brandName": "Merchant",
"supportEmail": "support@merchant.example",
"brandingBrandColor": "#111827",
"brandingAccentColor": "#14b8a6",
"paymentLinkTheme": "system",
"paymentLinkDomain": "pay.merchant.example",
"emailSendingDomain": "mail.merchant.example",
])
try await makepay.refreshBrandingDomains(kind: .all)try await makepay.getSettings()
try await makepay.updateSettings([
"callbackUrl": "https://merchant.example/webhooks/makepay",
])
try await makepay.listDestinationAssets()
try await makepay.listWebhookRequests(query: ["limit": "25"])Most API methods return MakePayResponse, which is a
[String: JSONValue]. Use typed payload structs for common create/update
requests and MakePayObject for flexible partner API fields that may evolve
between SDK releases.
do {
let settings = try await makepay.getSettings()
let callbackURL = settings["callbackUrl"]?.stringValue
} catch MakePayError.apiError(let statusCode, let message, let response) {
print("MakePay API error", statusCode, message, response ?? .null)
} catch {
print("Unexpected MakePay SDK error", error)
}The SDK raises MakePayError.invalidConfiguration before network calls when
required credentials are empty, and MakePayError.apiError for non-2xx API
responses with the decoded response body when available.
Read the exact raw request body before parsing JSON.
let event = try MakePayWebhook.parse(
rawBody: rawBodyData,
signatureHeader: request.headers["x-makepay-signature"],
secret: ProcessInfo.processInfo.environment["MAKEPAY_WEBHOOK_SECRET"]!
)
if event["event"]?.objectValue?["type"] == "status_changed" {
// Update your local order status.
}Use MakePayWebhook.verify when you only need a boolean result.
Inject a custom MakePayHTTPTransport to unit test request paths, headers, and
JSON bodies without sending network traffic.
final class MockTransport: MakePayHTTPTransport, @unchecked Sendable {
private(set) var requests: [URLRequest] = []
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
requests.append(request)
let response = HTTPURLResponse(
url: request.url!,
statusCode: 200,
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"]
)!
return (Data(#"{"ok":true}"#.utf8), response)
}
}
let transport = MockTransport()
let client = MakePayClient(keyID: "mk_test", keySecret: "mksec_test", transport: transport)
_ = try await client.getSettings()| Area | SDK methods |
|---|---|
| Payment links | createPaymentLink, listPaymentLinks, getPaymentLink, updatePaymentLink, sendPaymentRequestEmail |
| Donations | createDonationLink, listDonationLinks, getDonationLink, updateDonationLink |
| Anonymous links | createAnonymousPaymentLink, createAnonymousMakePayPaymentLink |
| Checkout | hosted, embedded, modal, button, iframe, and donation URL helpers |
| Customers | listCustomers, upsertCustomer, createCustomerPortal |
| Subscriptions | listSubscriptions, createSubscription |
| POS terminals | listPosTerminals, createPosTerminal, getPosTerminal, updatePosTerminal |
| Products | listProducts, createProduct, getProduct, updateProduct, listProductDownloads, createProductDownload |
| Simple Shop | getShop, updateShop, getShopBuilder, updateShopBuilder, getShopDomain, updateShopDomain, coupons, orders |
| Bookkeeping | summary, invoice, expense, document upload/OCR, and reconciliation methods |
| Branding | getBranding, updateBranding, refreshBrandingDomains |
| Operations | getSettings, updateSettings, listDestinationAssets, listWebhookRequests |
| Webhooks | MakePayWebhook.verify, MakePayWebhook.parse |
- SDK payloads use camelCase. Some API routes also accept snake_case for compatibility, but new integrations should send camelCase.
- Use strings for decimal money values when precision matters, for example
"129.99"instead of129.99. - Dates are ISO strings. Date-only fields, such as invoice
issueDate, should useYYYY-MM-DD. - IDs are usually public
uidvalues. Bookkeeping detail endpoints accept an internal UUID or public UID. - API methods throw
MakePayError.apiErrorfor non-2xx responses with the HTTP status, message, and decoded response body.
Before tagging a release, maintainers should run:
swift build
swift run MakePayPackageCheckSwift Package Manager users receive a new version when the corresponding Git tag is published.