Checkout
Calculate a member's selected Pricing, then submit the applicable payment request using existing payment configuration. Use the User subscriptions and Pricing selection to keep calculation, transaction state and content access separate. Exact API subscription names refer to Pricings in these requests.
| Task | Operation | Context |
|---|---|---|
| Display the current member-specific price | Calculate | Existing member and selected resource/items. |
| Prepare a Stripe SetupIntent | Setup intent | Existing Stripe customer and configured resource. |
| Confirm the configured Stripe payment branch | Confirm intent | Existing provider intent and local transaction. |
| Calculate one Pricing without member personalization | Pricing calculation | Valid resource and existing Pricing. |
| Submit selected items for payment | Payment | User role, member/resource and existing configured payment source. |
Calculate the selected items
POST /api/v1/payment/calculate-price
Use this to display the current price, discount and taxes for a member's selection. It does not create checkout transactions, purchases or memberships. It also skips promotion-activation recording. It does not reserve a price or promise a later payment result.
Before you call
Select an existing Pricing ID from the catalog. Establish ordinary identity or the configured Firebase identity. This calculation is guest-granted by the action ACL but still requires a resolved active user context; guest permission is not anonymous personalized pricing. The examples use a member. Item resource fallback does not establish anonymous member-token resolution.
Request
POST a nonempty JSON array of item objects, not an object wrapping an items property. The server obtains prices from stored records; there is no client-supplied amount to lock a quote. Use sample configuration and runtimes. Examples use an API base containing the scheme/host only.
| Name | Location | Type | Requirement | Meaning / constraint |
|---|---|---|---|---|
resource | header | string | required in member examples; per-item fallback for calculation | Resource public key. Ordinary member-token lookup needs this context. Payment requires it; calculation also accepts per-item resource selection but taxes/payment-source validation still use header context. |
token | header | string | member examples | Existing Wallkit member-session token. |
firebase-token | header | string | conditional | For a Firebase-enabled resource, supply its configured Firebase ID token alongside token; see credentials. |
[].item_type | body | string | required, nonempty | subscription for a Pricing; also recognizes content, bundle, ti_event_ticket, with the limitations below. |
[].item_key | body | integer / string | required, nonempty | Pricing, bundle or ticket numeric ID; content key string. Numeric Pricing IDs are cast to integer. |
[].item_resource | body | string | optional | Selects the item's resource by public key instead of the header resource. Keep it aligned with the header: payment records/provider context and taxes still use header context. |
[].promo | body | string | optional, nonempty value validated | Existing promotion code. Validation can succeed without a discount applying to the selected item. |
[].invite | body | string | private Pricing only | Existing invite in the item's resource that names the selected Pricing. |
[].payment_method_id | body | integer | optional | Existing Wallkit payment-method ID belonging to this user/resource. Send a JSON number; string values do not select this branch. Takes precedence over user_card_id for payment selection. |
[].user_card_id | body | numeric ID | optional, truthy | Existing saved-card ID validated against this user/resource. It is also validated when both selectors are supplied. Never send raw card details here. |
Shared selection requirements
There is no quantity field in this contract. Repeated ticket items are counted for ticket validation. Ticket requests require an enabled event system and header resource. Although bundle passes item-type validation, the subsequent tax calculation has no bundle branch; do not depend on a successful bundle checkout through these operations.
Pricing selection requires an active record in its Plan's resource and a configured currency. An existing relationship to that exact Pricing blocks the request; a once-only Pricing also checks history. Applicable cascade access can block an already-covered Plan/content. Calculation does not bypass these checks. A Pricing ID is distinct from a membership ID.
Result
HTTP 200 JSON with fields at the top level, without an items wrapper. See the calculation fields for every field and nested tax/trial/upgrade meaning.
total_price sums each discounted subtotal plus taxes. discount and total_taxes are accumulated separately. currency is overwritten for each item, leaving the last item's currency; mixed currencies are not a converted total. Keep one currency per displayed calculation. purchases here contains calculated item descriptions, not saved purchase records.
Example: price Pricing 2001
Assume active public Pricing 2001 is available to this member and has price 1200, currency USD, no applied trial/upgrade/promotion and zero configured taxes. The numeric values use the integration's configured units; 1200 is not a claim of $12.00.
cURL
curl "${WALLKIT_API_BASE}/api/v1/payment/calculate-price" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
--data '[{"item_type":"subscription","item_key":2001}]'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/payment/calculate-price", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify([{"item_type":"subscription","item_key":2001}])
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/payment/calculate-price")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"Content-Type": "application/json"
}
body = [{"item_type":"subscription","item_key":2001}]
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt:
{"total_price":1200,"discount":0,"total_taxes":0,"currency":"USD","purchases":[{"item_type":"subscription","item_key":2001,"price":1200,"base_price":1200,"discount":0,"discounted_price":1200,"total_taxes":0,"currency":"USD","promo":null,"upgrade_price":{"is_applied_upgrade_price":false,"price_margin":0},"trial":{"is_applied":false,"price":null,"period":null,"iterations":null,"currency":"USD"}}]}
The excerpt omits title/description/period and tax objects, defined in the linked projection. Trial configuration can be present even when is_applied is false. Display the returned amount/currency using your configured unit convention; no purchase has happened.
Consequential alternate: Pricing already purchased
HTTP 406:
{"error":"incorrect_subscription","error_description":"This subscription already purchased","req_guid":"example-request"}
An existing relationship to this exact Pricing blocks calculation too. Read the member's current profile/memberships; do not present the same selection as a fresh purchase.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Missing identity, inactive account or locked/suspended resource relationship. | Check existing member-session/resource context; ask the administrator about suspension/locks. |
| 406 | incorrect_data | Missing or unreadable JSON body. | Send the nonempty array shown in the example with JSON Content-Type. |
| 406 | incorrect_items | Missing/invalid item fields or selected payment source fails validation. | Correct the item type/key; use a payment-source ID belonging to this user/resource. |
| 406 | incorrect_subscription | Pricing missing/inactive, private invite mismatch, already purchased, once-only history or applicable cascade access. | Check the selected Pricing/resource/invite; read current account/access state before choosing another purchase. |
| 406 | incorrect_currency | Selected item has no configured currency. | Ask the integration administrator to correct its catalog configuration. |
| 406 | incorrect_promo | Promotion validation, dates, resource or usage limits fail. | Correct/remove the code and obtain a fresh calculation for the intended item. |
| 406 | incorrect_content / incorrect_bundle | Missing or already-owned content/bundle, or applicable content cascade access. | Check the existing record and current access; do not purchase merely to repair an access decision. |
| 406 | ti_event_resource / incorrect_ti_event / incorrect_ti_event_ticket | Event system, ticket availability or ticket validation fails. | Check enabled resource configuration, ticket identity and repeated ticket count. |
| 406 | incorrect_resource | No valid header/per-item resource. | Correct the public key and align item/header context. |
| 406 | buy_items_error | Calculation yields no items. | Supply a valid nonempty selection. |
| 406 | incorrect_taxes | Tax calculation exception. | Ask the administrator to check tax/payment configuration before submitting a payment. |
| 500 | unknown_error | Unhandled calculation failure, including unsupported downstream item handling. | Retain the request reference and ask the integration owner to inspect the failure; do not interpret it as zero price. |
Next task
Review the amount and selection with the member, then use payment only with the applicable existing provider/source configuration. Payment recalculates and can differ with changed records, eligibility or tax context.
Submit a payment selection
POST /api/v1/payment
This operation recalculates items, can call a payment provider and saves transaction/purchase records. Successful transaction branches attempt to apply content/bundle access, memberships or ticket passes. A successful HTTP response alone is not a completed charge or guaranteed membership.
Before you call
Requires the user role or inherited permission, active member context and a valid header resource. The resource must already have its applicable payment provider configuration and, for a positive charge, a saved source for this member/resource. default_paysystem defaults to stripe; the source also selects braintree and square operators. This describes configured source branches, not current provider compatibility or onboarding steps. If no explicit source is selected, payment attempts the existing default method, then card.
Request
Send the selected item array using the calculation request format, not its response object. Payment requires the header resource. Stored records determine the recalculated amount; a client cannot lock a quote.
| Name | Location | Type | Requirement | Meaning / constraint |
|---|---|---|---|---|
resource | header | string | required | Resource public key. Ordinary member-token lookup needs this context. Payment requires it; calculation also accepts per-item resource selection but taxes/payment-source validation still use header context. |
token | header | string | member examples | Existing Wallkit member-session token. |
firebase-token | header | string | conditional | For a Firebase-enabled resource, supply its configured Firebase ID token alongside token; see credentials. |
[].item_type | body | string | required, nonempty | subscription for a Pricing; also recognizes content, bundle, ti_event_ticket, with the limitations below. |
[].item_key | body | integer / string | required, nonempty | Pricing, bundle or ticket numeric ID; content key string. Numeric Pricing IDs are cast to integer. |
[].item_resource | body | string | optional | Selects the item's resource by public key instead of the header resource. Keep it aligned with the header: payment records/provider context and taxes still use header context. |
[].promo | body | string | optional, nonempty value validated | Existing promotion code. Validation can succeed without a discount applying to the selected item. |
[].invite | body | string | private Pricing only | Existing invite in the item's resource that names the selected Pricing. |
[].payment_method_id | body | integer | optional | Existing Wallkit payment-method ID belonging to this user/resource. Send a JSON number; string values do not select this branch. Takes precedence over user_card_id for payment selection. |
[].user_card_id | body | numeric ID | optional, truthy | Existing saved-card ID validated against this user/resource. It is also validated when both selectors are supplied. Never send raw card details here. |
Apply the shared item and Pricing selection requirements. Payment additionally requires the header resource and provider/source context described above.
Result
Usually HTTP 200 JSON with transactions[]; a failed group selects HTTP 406 and also adds error: transaction_fail and error_description. The final response still includes processed transactions. HTTP 200 with status: requires_source_action returns the raw transaction model and stops this request before purchase creation for that group. See the payment projections; this branch is not the ordinary transaction-with-purchases projection.
Statuses succeeded, free and manual are treated as successful by Wallkit. Zero/nonpositive calculated total takes the free-checkout branch without a provider charge. Other status values are not a complete enum here. Successful status causes relationship creation attempts and a receipt-generation event request. Purchase or relationship failures can be caught without changing the HTTP result; receipt events do not establish generated/delivered receipts.
Standard Pricing application can replace an existing relationship in the same Plan; with resource single_subscription enabled, it can archive/delete other member memberships in that resource and clear applicable sponsorship state. When a deleted membership's resource enables auto_clear_content_views, deletion can also clear this member's content-view records across resources: that cleanup is selected by user ID alone. New memberships/history and cache changes depend on configuration and selected Pricing. Do not infer a refund, safe replay or atomic rollback across provider and local writes.
Example: pay for Pricing 2001
Use existing saved Wallkit payment-method ID 3001 for this member/resource. Assume the recalculated amount remains 1200 USD in configured units and the provider reports succeeded. The examples below only illustrate the request; they were not executed.
cURL
curl "${WALLKIT_API_BASE}/api/v1/payment" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
--data '[{"item_type":"subscription","item_key":2001,"payment_method_id":3001}]'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/payment", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify([{"item_type":"subscription","item_key":2001,"payment_method_id":3001}])
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/payment")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"Content-Type": "application/json"
}
body = [{"item_type":"subscription","item_key":2001,"payment_method_id":3001}]
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt:
{"transactions":[{"id":4001,"status":"succeeded","payment_method_id":3001,"amount":1200,"currency":"USD","error":null,"purchases":[{"id":5001,"item_type":"subscription","item_key":"2001","item_id":2001,"transaction_id":4001,"relation_model":"user_plan_relationships","relation_id":6001}]}]}
This shows a transaction with a recorded purchase and membership reference. The full purchase also includes item and relation, and transaction fee/source fields; use their exact projections below. This excerpt does not prove all local writes completed or content permission. Use the separate content decision before serving an article.
Consequential alternate: more authentication required
HTTP 200 response excerpt (raw transaction branch):
{"transactions":[{"id":4001,"status":"requires_source_action","amount":1200,"currency":"USD"}]}
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Missing identity, inactive account or locked/suspended resource relationship. | Check existing member-session/resource context; ask the administrator about suspension/locks. |
| 406 | incorrect_data | Missing or unreadable JSON body. | Send the nonempty array shown in the example with JSON Content-Type. |
| 406 | incorrect_items | Missing/invalid item fields or selected payment source fails validation. | Correct the item type/key; use a payment-source ID belonging to this user/resource. |
| 406 | incorrect_subscription | Pricing missing/inactive, private invite mismatch, already purchased, once-only history or applicable cascade access. | Check the selected Pricing/resource/invite; read current account/access state before choosing another purchase. |
| 406 | incorrect_currency | Selected item has no configured currency. | Ask the integration administrator to correct its catalog configuration. |
| 406 | incorrect_promo | Promotion validation, dates, resource or usage limits fail. | Correct/remove the code and obtain a fresh calculation for the intended item. |
| 406 | incorrect_content / incorrect_bundle | Missing or already-owned content/bundle, or applicable content cascade access. | Check the existing record and current access; do not purchase merely to repair an access decision. |
| 406 | ti_event_resource / incorrect_ti_event / incorrect_ti_event_ticket | Event system, ticket availability or ticket validation fails. | Check enabled resource configuration, ticket identity and repeated ticket count. |
| 404 | resource_not_exists | Invalid header resource. | Check the public resource key for this member/provider integration. |
| 406 | items_currency | Grouping lacks currency. | Ask the administrator to correct the selected item currency. |
| 406 | buy_items_error | Calculation yields no purchase items. | Supply a valid nonempty selection. |
| 406 | transaction_fail, with transactions[] | A processed group was not successful. Earlier groups/promotion records may already exist. | Inspect each transaction and error; reconcile recorded state with the integration owner before another payment attempt. |
| 200 | Transaction requires_source_action | Provider authentication remains outstanding. | Use the configured provider authentication flow and retain the transaction ID; do not treat it as paid. |
| 500 | unknown_error | Unhandled recalculation failure. | Retain the request reference and ask the integration owner to inspect it before another attempt. |
Tax exceptions are not given calculation's dedicated incorrect_taxes response here; they reach the generic recalculation failure handling. Exceptions outside that block can also interrupt processing without a stable handler-specific error body. No retry/idempotency contract is established.
Next task
Inspect the returned transaction and purchase relationship, then request the content-access decision for the intended article. Let your application serve or withhold content from that decision. Stored purchase records alone do not authorize serving it.
Create a Stripe SetupIntent
POST /api/v1/payment/stripe/setup-intent
Use this only in the existing Stripe integration to create a provider SetupIntent for an existing Stripe customer. This request calls the provider; it does not submit a Pricing selection, create a Wallkit purchase/membership or establish a settled payment.
Before you call
Requires active member context, user role or inherited permission, valid header resource and existing Stripe configuration. Supply the customer identifier already associated with the intended integration. The request sends that identifier to Stripe directly; there is no handler-level lookup proving it belongs to the current Wallkit member. Your trusted integration must retain the correct customer/resource association. This operation does not create or attach a customer for you.
Request
POST JSON object. Unlike item-array payment, this request has a single provider customer field.
| Name | Location | Type | Requirement | Meaning / constraint |
|---|---|---|---|---|
resource | header | string | required | Resource public key for the configured Stripe integration. |
token | header | string | member context | Existing Wallkit member-session token; user role or inherited permission. |
firebase-token | header | string | Firebase-enabled context | Configured provider ID token alongside the Wallkit token; see credentials. |
customer_id | body | provider identifier string | needed for the provider request | Existing Stripe customer identifier, such as the synthetic cus_example. No separate field-presence/type validator in this handler; provider failures become incorrect_setup_intent. |
Result
HTTP 201 JSON with stripe_intent: the provider-returned SetupIntent object, passed through without a fixed Wallkit projection. Its properties/types/statuses depend on that configured provider integration; this source does not define a portable schema. Keep its returned data within that flow. A created setup object is not a transaction, saved Wallkit source, payment confirmation or entitlement.
Example: prepare the existing customer
Use the sample runtimes. cus_example is synthetic; the real identifier must come from the existing integration.
cURL
curl "${WALLKIT_API_BASE}/api/v1/payment/stripe/setup-intent" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"customer_id":"cus_example"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/payment/stripe/setup-intent", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify({"customer_id":"cus_example"})
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/payment/stripe/setup-intent")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"Content-Type": "application/json"
}
body = {"customer_id":"cus_example"}
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 201 envelope excerpt; all provider-object properties omitted:
{"stripe_intent":{}}
The empty object is an omission in this excerpt, not a claim that the real returned SetupIntent is empty. The outcome is creation of a provider setup object; adapt its full returned properties using the configured Stripe flow. No Wallkit membership or charge result follows from this envelope.
Consequential alternate: provider setup fails
HTTP 406 response shape excerpt:
{"error":"incorrect_setup_intent","req_guid":"example-request"}
The full result includes error_description with the caught exception message. There is no stable provider-message enum; inspect that description and check existing customer/resource/mode configuration before another attempt. A failed local response does not promise absence of provider effects.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Missing member, suspended account or locked resource relationship. | Repair existing identity/context or ask the administrator about restrictions. |
| 404 | resource_not_exists | Header resource invalid. | Check the supplied public resource key. |
| 406 | incorrect_data | Missing JSON object. | Send the customer_id JSON object shown above. |
| 406 | incorrect_setup_intent | Stripe initialization/create throws. | Check the full description, intended customer association and existing provider configuration; do not assume replay is safe. |
Next task
Complete the setup through the already configured Stripe integration and obtain its existing saved payment source before submitting a Pricing payment. This endpoint alone does not establish a usable saved source. The confirmation operation below is for an existing PaymentIntent/local transaction, not this SetupIntent.
Confirm an existing Stripe payment branch
POST /api/v1/payment/stripe/confirm-intent
Use this only when the configured integration supplies an existing Stripe PaymentIntent identifier and a matching local Wallkit transaction. It is not a universal completion step for every payment or a way to confirm the SetupIntent above.
Before you call
Requires active member context, user role or inherited permission, valid resource and configured Stripe account/mode. The provider intent must already match a local transaction's provider transaction_id. The handler finds that record by provider ID without a user/resource ownership comparison, then uses the current member/resource for purchase processing and events. Keep the intent/local transaction/member/resource association under your integration's control; do not pass an arbitrary provider ID.
Local status is set to succeeded and purchase/relationship creation is attempted before provider retrieval/confirmation. The provider answer is saved without deriving local status from its returned status. Therefore HTTP 200/local succeeded does not independently prove provider settlement. Membership replacement/deletion can have the payment side effects, including cross-resource view cleanup when auto_clear_content_views is enabled. No cross-provider/local rollback, retry safety or single-use guarantee is established.
Request
POST JSON object.
| Name | Location | Type | Requirement | Meaning / constraint |
|---|---|---|---|---|
resource | header | string | required | Resource public key for the configured Stripe integration. |
token | header | string | member context | Existing Wallkit member-session token; user role or inherited permission. |
firebase-token | header | string | Firebase-enabled context | Configured provider ID token alongside the Wallkit token; see credentials. |
id | body | provider identifier string | needed | Existing Stripe PaymentIntent ID, not Wallkit numeric transaction ID. Used to find the local transaction and retrieve the intent. |
payment_method | body | provider identifier string | needed | Existing Stripe payment-method identifier, not Wallkit numeric payment_method_id. Passed to provider confirm. No separate field-presence/type validator. |
Result
HTTP 200 JSON transactions[] with one transaction using the ordinary payment/base purchase projection. The result does not include the provider answer in this projection, though it is saved locally. It requests transaction/conditional renewal events and receipt generation; none proves event completion or receipt delivery. Caught relationship failures can leave missing relation information.
Example: existing intent and payment method
Use the sample runtimes. Assume pi_example already names local transaction 4001 for the current member/resource, whose stored item is Pricing 2001; pm_example is its intended provider source. Do not substitute local IDs in the body.
cURL
curl "${WALLKIT_API_BASE}/api/v1/payment/stripe/confirm-intent" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "token: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"id":"pi_example","payment_method":"pm_example"}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/payment/stripe/confirm-intent", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
token: process.env.USER_TOKEN,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify({"id":"pi_example","payment_method":"pm_example"})
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/payment/stripe/confirm-intent")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"token": os.environ["USER_TOKEN"],
"Content-Type": "application/json"
}
body = {"id":"pi_example","payment_method":"pm_example"}
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt:
{"transactions":[{"id":4001,"status":"succeeded","amount":1200,"currency":"USD","purchases":[{"id":5001,"item_type":"subscription","item_key":"2001","item_id":2001,"transaction_id":4001,"relation_id":6001,"relation_model":"user_plan_relationships"}]}]}
This describes local recorded state from the operation. It does not assert that the provider returned settled/succeeded status; the handler does not copy that status into the local record. Use the existing provider/account reconciliation flow before displaying financial completion, and the separate access decision before serving content.
Consequential alternate: original transaction missing
HTTP 406:
{"error":"confirm_stripe_payment_intent_error","error_description":"Not found original payment intent transactions","req_guid":"example-request"}
Check that id is the provider intent identifier already recorded in the intended local transaction. Creating another charge does not repair this lookup.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 401 | auth_failed / auth_access_fail | Identity/context restrictions. | Check existing member/resource context or administrator restrictions. |
| 404 | resource_not_exists | Invalid header resource. | Correct the supplied resource key. |
| 409 | incorrect_data | Missing JSON object. | Send id and payment_method as shown; preserve their provider identity. |
| 406 | confirm_stripe_payment_intent_error | Missing original transaction, provider retrieval/confirm or other caught failure. | Read description; check intended intent/source/account/mode and reconcile local/provider state before another attempt. Local changes can precede the provider failure. |
Next task
Inspect the existing local transaction/account state and provider result through the configured integration, then request content access for the article. Receipt-generation acknowledgement is separate from a downloadable receipt; the User subscriptions and Pricing selection connects those reads.
Calculate one Pricing without member personalization
POST /api/v1/calculate-price/subscriptions/{id}
Use this guest-granted operation to calculate an existing Pricing in the selected resource. It uses a fresh user object, even when member credentials are supplied. It does not enforce the member's already-purchased/once-only/cascade/private-invite checks used by member calculation. Its result does not establish purchase eligibility or the eventual member-specific charge.
Before you call
Supply a valid resource public key and an active Pricing with a Plan in that resource and a configured currency. Obtain its numeric ID from the catalog. No member token is required by this action. Configured trial eligibility can apply without this member's history, and member upgrade credit is not personalized. The tax helper receives no member country; country-dependent tax can differ from member calculation/payment.
Request
POST JSON object; {} is the minimal object with no promotion. The Pricing ID is in the path, not an item array. A present promo property is validated even if empty/null; omit it to calculate without a code.
| Name | Location | Type | Requirement | Meaning / constraint |
|---|---|---|---|---|
resource | header | string | required | Public key selecting the Pricing's Plan resource and tax context. |
id | path | integer | required | Existing Pricing ID; registered route restricts digits. |
promo | body | string | optional | Existing promotion code for the resource. If present, validation can return invalid_promo; acceptance does not guarantee the Pricing receives a discount. |
Result
HTTP 200 JSON with one calculated item at the top level. Use the single-Pricing projection; there is no purchases array or saved purchase. taxes is null in this projection, although total_taxes participates in total_price. Do not infer zero tax from taxes:null.
Example: display Pricing 2001's configured calculation
Assume active public Pricing 2001 in this resource has price 1200 USD, no applicable trial and zero taxes. Use sample runtimes; the examples need the resource, not member identity.
cURL
curl "${WALLKIT_API_BASE}/api/v1/calculate-price/subscriptions/2001" \
-X POST \
-H "resource: ${RESOURCE_KEY}" \
-H "Content-Type: application/json" \
--data '{}'
JavaScript
// Node.js 18+; built-in fetch.
async function main() {
const url = new URL("/api/v1/calculate-price/subscriptions/2001", process.env.WALLKIT_API_BASE);
const headers = {
resource: process.env.RESOURCE_KEY,
"Content-Type": "application/json"
};
const response = await fetch(url, {
method: "POST", headers, body: JSON.stringify({})
});
console.log(response.status, await response.json());
}
main().catch(console.error);
Python
# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urljoin
from urllib.request import Request, urlopen
url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/calculate-price/subscriptions/2001")
headers = {
"resource": os.environ["RESOURCE_KEY"],
"Content-Type": "application/json"
}
body = {}
request = Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urlopen(request) as response:
print(response.status, json.load(response))
except HTTPError as error:
print(error.code, json.load(error))
HTTP 200 response excerpt:
{"item_type":"subscription","item_key":2001,"price":1200,"discount":0,"discounted_price":1200,"base_price":1200,"total_taxes":0,"total_price":1200,"currency":"USD","taxes":null,"item_is_allowed_autorenew":true,"item_is_allowed_next_subscription":false}
The example omits descriptive/trial/upgrade fields, defined in the projection. The next-Pricing flag is the inverse of configured autorenew allowance in this response, not a member's actual next-Pricing choice. Before presenting a member's payable amount, perform member calculation with that identity.
Consequential alternate: wrong resource for the Pricing
HTTP 409:
{"error":"subscription_resource_not_match","error_description":"Subscription resource not match","req_guid":"example-request"}
Select a Pricing belonging to the supplied resource. A different member token does not fix the Plan/resource mismatch.
Recovery
| HTTP | Code / response | Cause | Next action |
|---|---|---|---|
| 409 | resource_required | Header resource unresolved. | Check the supplied public key. |
| 409 | incorrect_data | Missing JSON object. | Send {} or the promo object. |
| 409 | invalid_promo | Present promo fails validation. | Correct/remove the code; omission is different from a present empty value. |
| 409 | subscription_not_found / subscription_plan_not_found | Pricing or associated Plan missing. | Choose an existing catalog Pricing; ask the administrator about missing Plan configuration. |
| 409 | subscription_resource_not_match | Pricing Plan belongs to another resource. | Align selected Pricing and header resource. |
| 409 | subscription_not_active / subscription_currency_empty | Inactive Pricing or missing currency. | Choose an active configured Pricing or ask the administrator to fix the catalog. |
| 406 | incorrect_promo / incorrect_taxes | Discount or tax calculation exception. | Read the description and check existing promotion/tax configuration. |
| 409 | calculate_subscription_error | Other caught calculation failure. | Ask the integration owner to inspect configuration; do not treat it as free or eligible. |
Next task
Establish member identity and use member item calculation to obtain its actual checks and pricing context before submitting a payment.