Skip to content

CyberSource ​

CyberSource Secure Acceptance is a hosted checkout for card payments, in MMK or any other currency.

CallWhat it doesReturns
cs.initiate(data)Signed form posted to the hosted checkoutFormPayment
cs.handle_callback(request)Verify the result postPaymentCallback

CyberSource has no status API in this package: the callback is the only payment result.

CyberSource makes no network calls, so the one class serves sync and async code and nothing is awaited.

Responses shows what CyberSource puts in each result.

How it works ​

CyberSource posts the result twice, to your backoffice URL and through the browser to your receipt page, and both are verified the same way.

CyberSource: signed form, hosted checkout, two result postsCustomerYour appCyberSourceCheck outSigned form, auto-submitscs.initiate(data)Post to the hosted checkoutPOST /pay, then the card formBackoffice postoverride_backoffice_post_urlVerified callback is proofcs.handle_callback(request)Browser posts the receiptoverride_custom_receipt_pageSame signature checkcs.handle_callback(request)Show the receipt
CyberSource: signed form, hosted checkout, two result posts

Initiating a Payment ​

python
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from python_myanmar_payments import (
    CyberSource,
    CyberSourceConfig,
    CyberSourcePaymentData,
    CyberSourceTransactionType,
)

from shop.models import Order

cs = CyberSource(
    CyberSourceConfig(
        profile_id="...",
        access_key="...",
        secret_key="...",
    )
)


def cybersource_checkout(request, order_id: int):
    order = get_object_or_404(Order, pk=order_id)

    data = CyberSourcePaymentData(
        order_id=f"ORDER_{order.id}",
        amount=10000,
        callback_url="https://shop.test/payments/cybersource/callback",
        currency="MMK",
        transaction_type=CyberSourceTransactionType.SALE,
        locale="en-us",
        return_url="https://shop.test/payments/cybersource/receipt",
        cancel_url="https://shop.test/checkout",
    )

    payment = cs.initiate(data)

    # The page posts the signed form to CyberSource on load.
    return HttpResponse(payment.to_html())

CyberSourcePaymentData ​

FieldTypeRequiredRules
order_idstrYesAt most 50 characters, sent as reference_number
amountAmount | int | str | DecimalYesOrder total in currency, 0 or more, any number of decimals, at most 15 characters, e.g. 10000 or Amount.parse("10.50")
callback_urlstrYesAbsolute http or https URL CyberSource posts the result to. At most 255 characters; CyberSource may require HTTPS in production
currencystrYesAny three-letter uppercase ISO 4217 code, e.g. MMK
transaction_typeCyberSourceTransactionTypeYesCyberSourceTransactionType.SALE ("sale"), .AUTHORIZATION ("authorization"), .SALE_AND_CREATE_TOKEN ("sale,create_payment_token") or .AUTHORIZATION_AND_CREATE_TOKEN ("authorization,create_payment_token")
localestrYesHosted page language as a CyberSource locale code, e.g. en-us
return_urlstrNoReceipt page for the customer (absolute http or https URL). At most 255 characters
cancel_urlstrNoPage shown when the customer cancels (absolute http or https URL). At most 255 characters

Amounts and Currencies ​

CyberSource is multi-currency and accepts decimals. For another currency, pass an Amount with the currency: amount=Amount.parse("10.50"), currency="USD".

Form Encoding ​

CyberSource expects the form as application/x-www-form-urlencoded. payment.enctype carries it; use it if you render the form yourself.

Handling Callbacks ​

CyberSource posts a form to callback_url. The same check works for the browser post to your receipt page.

python
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from python_myanmar_payments import CallbackRequest, SignatureVerificationError


# POST /payments/cybersource/callback
@csrf_exempt
def cybersource_callback(request):
    callback_request = CallbackRequest(
        body=request.body,
        headers=request.headers,
        query=request.META.get("QUERY_STRING", ""),
    )
    try:
        callback = cs.handle_callback(callback_request)
    except SignatureVerificationError:
        return HttpResponse("invalid callback", status=400)

    if callback.is_successful():
        # callback.order_id is your req_reference_number
        # callback.gateway_reference is CyberSource's transaction_id
        ...

    ack = callback.acknowledgement
    return HttpResponse(ack.body, status=ack.status, headers=ack.headers)

Only signed fields are trusted: decision and req_reference_number must be listed in signed_field_names, transaction_id and the amount are read only when they are signed, and raw keeps only the signed fields plus signature. An unsigned extra field, such as decision=ACCEPT added to a re-posted checkout form, can't change the result.

Responses ​

What CyberSource puts in each field. See Results and PaymentCallback & Status for the full classes. A field the gateway didn't send is None. CyberSource posts form fields, so every raw value is a str, exactly as sent.

initiate() → FormPayment ​

Field / MethodCyberSource value
flowPaymentFlow.FORM
order_idYour data.order_id
action{base_url}/pay, e.g. https://secureacceptance.cybersource.com/pay
fieldsThe signed fields below, in signing order. Post them unchanged
enctypeapplication/x-www-form-urlencoded
to_html()A full HTML page that posts fields to action on load
Form fieldValue
access_keyconfig.access_key
profile_idconfig.profile_id
transaction_uuidA random ID, new for every call
signed_field_namesThe field names in this table except signature, comma-separated
signed_date_timeUTC time, e.g. 2026-10-08T09:30:00Z
localedata.locale
transaction_typedata.transaction_type
reference_numberdata.order_id
amountdata.amount, e.g. 10000
currencydata.currency
override_custom_receipt_pagedata.return_url, "" when unset
override_backoffice_post_urldata.callback_url
override_custom_cancel_pagedata.cancel_url, "" when unset
signatureBase64 HMAC-SHA256 of the signed fields

initiate() makes no HTTP call, so it is never awaited, and CyberSource takes no HTTP options. FormPayment has no raw: nothing is sent to CyberSource until the customer's browser posts the form.

handle_callback() → PaymentCallback ​

Field / MethodCyberSource value
order_idCyberSource req_reference_number (your order_id)
statusdecision mapped, see Statuses
gateway_statusCyberSource decision, trimmed and uppercased, e.g. ACCEPT
gateway_referenceCyberSource transaction_id. None when it is not signed
amountCyberSource auth_amount, falling back to req_amount when it is missing or empty, e.g. 10000. Signed values only
rawThe signed fields of the verified post plus signature, e.g. decision, reason_code, message, transaction_id, auth_amount, auth_code, req_reference_number, req_amount, req_currency, req_transaction_uuid, signed_field_names, signed_date_time. Unsigned fields are left out
acknowledgementHTTP 200, empty body, Content-Type: text/plain

Statuses ​

CyberSource decisionPaymentStatus
ACCEPTSUCCESSFUL
REVIEWPENDING
DECLINE, ERRORFAILED
CANCELCANCELED
anything elseUNKNOWN

Errors ​

CallRaisesWhen
initiate()InvalidPaymentDataErrorCyberSource.validate(data) fails. Nothing is signed
handle_callback()SignatureVerificationErrorsignature doesn't match, a field listed in signed_field_names is missing or holds an object or a list, or decision or req_reference_number isn't signed

CyberSource makes no HTTP calls, so nothing raises ApiError.

Released under the MIT License, except where a package says otherwise.