Package: xumm-sdk-py
XUMM SDK (Python)Interact with the XUMM SDK from Python environments.⚠️ Please note: The XUMM SDK (XUMM API in general) is for BACKEND USE only. Please DO NOT use your API credentials in a FRONTEND environment.To implement the XUMM SDK (or XUMM API directly) in your own web project, make sure your frontend calls your own backend, where the follow up communication with the XUMM SDK (or XUMM API) will take place. Your XUMM credentials should never be publicly available.Getting StartedUsagePayloadsApp StorageHelper MethodsDevelopmentGetting StartedInstallationVia pip:pip3installxumm-sdk-pyUsageimportxummsdk=xumm.XummSdk()# Or with manually provided credentials (instead of using dotenv):sdk=xumm.XummSdk('XUMM_APIKEY','XUMM_APISECRET')After constructing the SDK, you can call the methods:sdk.*for the helper methods (see below)sdk.payload.*to get/update/create payloads for users to signsdk.storage.*for your XUMM app storage (to store meta info for headless applications)The SDK will look in your environment or dotenv file (.env) for theXUMM_APIKEYandXUMM_APISECRETvalues. A sample dotenv can be foundhere. Alternatively you can provide your XUMM API Key & Secret by passing them to the XummSdk constructor.NOTE: If both your environment and the SDK constructor contain credentials, the values provided to the constructor will be used.PayloadsIntroPayloads are the primary reason for the XUMM API (thus this SDK) to exist. TheXUMM API Docs explain 'Payloads'like this:An XRPL transaction "template" can be posted to the XUMM API. Your transaction tample to sign (so: your "sign request") will be persisted at the XUMM API backend. We now call it a aPayload. XUMM app user(s) can open the Payload (sign request) by scanning a QR code, opening deeplink or receiving push notification and resolve (reject or sign) on their own device.A payload can contain an XRPL transaction template. Some properties may be omitted, as they will be added by the XUMM app when a user signs a transaction. A simple payload may look like this:payload={'txjson':{'TransactionType':'Payment','Destination':'rwiETSee2wMz3SBnAG8hkMsCgvGy9LWbZ1','Amount':'1337'}}As you can see the payload looks like a regular XRPL transaction, wrapped in antxjsonobject, omitting the mandatoryAccount,FeeandSequenceproperties. They will be added containing the correct values when the payload is signed by an app user.Optionally (besidestxjson) a payload can contain these properties (PY definition):optionsto define payload options like a return URL, expiration, etc.custom_metato add metadata, user insruction, your own unique ID, ...user_tokento push the payload to a user (afterobtaining a user specific token)A more complex payloadcould look like this. Areference for payload options & custom metacan be found in theAPI Docs.Instead of providing atxjsontransaction, a transaction formatted as HEX blob (string) can be provided in atxblobproperty.sdk.payload.getsdk.payload.get(payload:Union[str,CreatedPayload]):->XummPayloadTo get payload details, status and if resolved & signed: results (transaction, transaction hash, etc.) you canget()a payload.Note! Please don't usepolling! The XUMM API offers Webhooks (configure your Webhook endpoint in theDeveloper Console) or usea subscriptionto receive live payload updates (for non-SDK users:Webhooks).You canget()a payload by:Payload UUIDpayload=sdk.payload.get('aaaaaaaa-bbbb-cccc-dddd-1234567890ab')Passing a created Payload object (see:sdk.payload.create)new_payload:xumm_types.created_payload={txjson:{...}}created=sdk.payload.create(new_payload)payload=sdk.payload.get(created)sdk.payload.get('aaaaaaaa-bbbb-cccc-dddd-1234567890ab')sdk.payload.createsdk.payload.create(payload:create_payload):->Union[CreatedPayload,None]To create a payload, atxjsonXRPL transaction can be provided. Alternatively, a transaction formatted as HEX blob (string) can be provided in atxblobproperty.See theintrofor more information about payloads.Take a look at theDeveloper Docs for more information about payloads.The response (see:Developer Docs) of asdk.payload.create()operation, a<CreatedPayload>json object, looks like this:{"uuid":"1289e9ae-7d5d-4d5f-b89c-18633112ce09","next":{"always":"https://xumm.app/sign/1289e9ae-7d5d-4d5f-b89c-18633112ce09","no_push_msg_received":"https://xumm.app/sign/1289e9ae-7d5d-4d5f-b89c-18633112ce09/qr"},"refs":{"qr_png":"https://xumm.app/sign/1289e9ae-7d5d-4d5f-b89c-18633112ce09_q.png","qr_matrix":"https://xumm.app/sign/1289e9ae-7d5d-4d5f-b89c-18633112ce09_q.json","qr_uri_quality_opts":["m","q","h"],"websocket_status":"wss://xumm.app/sign/1289e9ae-7d5d-4d5f-b89c-18633112ce09"},"pushed":true}Thenext.alwaysURL is the URL to send the end user to, to scan a QR code or automatically open the XUMM app (if on mobile). If auser_tokenhas been provided as part of the payload data provided tosdk.payload.create(), you can see if the payload has been pushed to the end user. A button "didn't receive a push notification" could then take the user to thenext.no_push_msg_receivedURL.Alternatively user routing / instruction flows can be custom built using the QR information provided in therefsobject, and a subscription for live status updates (opened, signed, etc.) using a WebSocket client can be setup by conneting to therefs.websocket_statusURL.Please note: this SDK already offers subscriptions. There's no need to setup your own WebSocket client, seePayload subscriptions: live updates.There's more information about thepayload workflowand apayload lifecyclein the Developer Docs.sdk.payload.cancelsdk.payload.cancel(payload:Union[str,XummPayload,CreatedPayload]):->Union[DeletedPayload,None]To cancel a payload, provide a payload UUID (string), a<XummPayload>(by performing asdk.payload.get()first) or a<CreatedPayload>(by using the response of asdk.payload.create()call). By cancelling an existing payload, the payload will be marked as expired and can no longer be opened by users.Please note:if a user already opened the payload in XUMM APP, the payload cannot be cancelled: the user may still be resolving the payload in the XUMM App, and should have a chance to complete that process.A response (generic API typeshere) looks like:response.result.cancelled# boolresponse.result.reason# XummCancelReasonresponse.meta# XummPayloadMetaresponse.custom_meta# XummCustomMetaPayload subscriptions: live updatesTo subscribe to live payload status updates, the XUMM SDK can setup a WebSocket connection and monitor live status events. Emitted events include:The payload is opened by a XUMM App user (webpage)The payload is opened by a XUMM App user (in the app)Payload expiration updates (remaining time in seconds)The payload was resolved by rejectingThe payload was resolved by accepting (signing)More information about the status update events & sample event datacan be found in the Developer Docs.Status updates can be processed by providing acallback functionto thesdk.payload.subscribe()method. Alternatively, the (by thesdk.payload_subscribe()method) returned raw websocket can be used to listen for WebSocketonmessageevents.The subscription will be closed by either:Returning non-void in thecallback functionpassed to thesdk.payload.subscribe()methodManually calling<PayloadSubscription>.resolve()on the object returned by thesdk.payload.subscribe()methodsdk.payload.subscribesdk.payload.subscribe(payload:Union[str,XummPayload,CreatedPayload],callback:on_payload_event):->PayloadSubscriptionIf a callback function is not provided, the subscription will stay active until the<PayloadSubscription>.resolve()method is called manually, eg. based on handling<PayloadSubscription>.websocket.onmessageevents.When a callback function is provided, for every paylaod specific event the callback function will be called with<SubscriptionCallbackParams>. The<SubscriptionCallbackParams>.dataproperty contains parsed JSON containing event information. Either by calling<SubscriptionCallbackParams>.resolve()or by returning a non-void value in thecallback functionthe subscription will be ended, and the<PayloadSubscription>.resolvedpromise will resolve with the value returned or passed to the<SubscriptionCallbackParams>.resolve()method.Resolving (by returning non-void in the callback or callingresolve()manually) closes the WebSocket client the XUMM SDK sets up 'under the hood'.The<PayloadSubscription>object looks like this:response.payload# XummPayloadresponse.resolved# Union[CallbackPromise, None]response.resolve# CallbackPromiseresponse.websocket# WSClientExamples:Async process after returning data in the callback functionAwait based on returning data in the callback functionAwait based on resolving a callback eventAwait based on resolving without using a callback functionsdk.payload.create_subscribesdk.payload.create_and_subscribe(payload:CreatePayload,callback:on_payload_event):->PayloadAndSubscriptionThe<PayloadAndSubscription>object is basically a<PayloadSubscription>object with the created payload results in thecreatedproperty:All information that applies onsdk.payload.create()andsdk.payload.create_and_subscribe()applies. Differences are:The input for asdk.payload.create_and_subscribe()call isn't a payload UUID / existing payload, but a paykiad to create.The response object also contains (<PayloadAndSubscription>.created) the response obtained when creating the payloadApp StorageApp Storage allows you to store a JSON object at the XUMM API platform, containing max 60KB of data.
Your XUMM APP storage is stored at the XUMM API backend, meaning it persists until you overwrite or delete it.This data is private, and accessible only with your own API credentials. This private JSON data can be used to store credentials / config / bootstrap info / ... for your headless application (eg. POS device).storage_set=awaitsdk.storage.set({'name':'Wietse','age':32,'male':True})print(storage_set)# Truestorage_get=sdk.storage.get()print(storage_get.data)# { 'name': 'Wietse', 'age': 32, 'male': True }storage_delete=sdk.storage.delete()print(storage_delete)# Truestorage_get_after_delete=sdk.storage.get()print(storage_get_after_delete.data)# NoneHelper methodssdk.ping()Thepingmethod allows you to verify API access (valid credentials) and returns some info on your XUMM APP:pong=sdk.ping()Returns<ApplicationDetails>:pong.quota# {}pong.application.name# 'My XUMM APP'pong.application.uuidv4# '00000000-1111-2222-3333-aaaaaaaaaaaa'pong.application.webhookurl# ''pong.application.disabled# 0pong.call.uuidv4# 'bbbbbbbb-cccc-dddd-eeee-111111111111'sdk.get_curated_assets()Theget_curated_assetsmethod allows you to get the list of trusted issuers and IOU's. This is the same list used to
populate the "Add Asset" button at the XUMM home screan.curated_assets=sdk.get_curated_assets()Returns<CuratedAssetsResponse>:curated_assets.issuers# [ 'Bitstamp', 'GateHub' ]curated_assets.currencies# [ 'USD', 'BTC', 'EUR', 'ETH' ]curated_assets.details.Bitstamp# {}curated_assets.details.GateHub# {}sdk.get_kyc_status()Theget_kyc_statusreturn the KYC status of a user based on a user_token, issued after the
user signed a Sign Request (from your app) before (see Payloads - Intro).If a user token specified is invalid, revoked, expired, etc. the method will always
returnNONE, just like when a user didn't go through KYC. You cannot distinct a non-KYC'd user
from an invalid token.Alternatively, KYC status can be retrieved for an XPRL account address: the address selected in
XUMM when the session KYC was initiated by.kyc_status=sdk.get_kyc_status('00000000-0000-0000-0000-000000000000')... or using an account address:kyc_status=sdk.get_kyc_status('rwu1dgaUq8DCj3ZLFXzRbc1Aco5xLykMMQ')Returns<str of PossibleKycStatuses>.Notes on KYC informationOnce an account has successfully completed the XUMM KYC flow, the KYC flag will be applied to the account even if the identity document used to KYC expired. The flag shows that the account wasonceKYC'd by a real person with a real identity document.Please note that the KYC flag provided by XUMM can't be seen as a "all good, let's go ahead" flag: it should be used asone of the data pointsto determine if an account can be trusted. There are situations where the KYC flag is stillTrue, but an account can no longer be trusted. Eg. when account keys are compromised and the account is now controlled by a 3rd party. While unlikely, depending on the level of trust required for your application you may want to mitigate against these kinds of fraud.sdk.get_transaction()Theget_transactionmethod allows you to get the transaction outcome (mainnet)
live from the XRP ledger, as fetched for you by the XUMM backend.Note: it's best to retrieve these resultsyourselfinstead of relying on the XUMM platform to get live XRPL transaction information! You can use thexrpl-pypackage to do this:tx_info=sdk.get_transaction(tx_hash)Returns:<XrplTransaction>.sdk.verify_user_tokens() / sdk.verify_user_tokens()Theverify_user_tokens(or single token:verify_user_token) method allows you to verify one or more User Tokens obtained from previous sign requests. This allows you to detect if you will be able to push your next Sign Request to specific users.some_token='691d5ae8-968b-44c8-8835-f25da1214f35'token_validity=sdk.verify_user_tokens([some_token,'b12b59a8-83c8-4bc0-8acb-1d1d743871f1','51313be2-5887-4ae8-9fda-765775a59e51'])ifsdk.verify_user_token(some_token).active:# Push, use `user_token` in payloadelse:# QR or Redirect (deeplink) flowReturns:<UserTokenValidity or [UserTokenValidity]>.DevelopmentInstall requirmentspipinstall-e".[develop]"BuildPlease note: at least Python version3.6+is required!To build the code, runpython setup.py install.DebuggingThe XUMM SDK will emit debugging info when invoked with a debug environment variable configured like:DEBUG=xumm-sdk*You'll see the XUMM SDK debug messages if you invoke your script instead of this:python3 main.pyExample:DEBUG=xumm-sdk* python3 main.pyLint & testLint the code using:python3-mflake8For running tests:python3test.pytests/