An Azul Webservices light wrapper for Python.
Project description
PyAzul
Python SDK (Software Development Kit) for the Azul Payment Gateway. This library, referred to as PyAzul, allows you to process payments, tokenize cards, and implement 3D Secure authentication with minimal setup by interacting with the external Azul API.
Installation
pip install pyazul
Quick Setup & Configuration
This section outlines the necessary setup to get PyAzul running, including environment configuration for API credentials and certificates.
Environment Variables (.env file)
Create a .env file in your project root with your Azul credentials and settings:
# Basic API Credentials (Required for all operations)
AUTH1=your_auth1
AUTH2=your_auth2
MERCHANT_ID=your_merchant_id
ENVIRONMENT=dev # 'dev' for testing, 'prod' for live transactions
# SSL Certificates (Required for all operations)
AZUL_CERT=/path/to/certificate.pem # Can be a file path or the direct PEM string content
AZUL_KEY=/path/to/key.pem # Can be a file path, direct PEM string content, or Base64 encoded PEM content
# Payment Page Settings (Optional - only if using Payment Page feature)
AZUL_AUTH_KEY=your_auth_key_for_page
MERCHANT_NAME=Your_Business_Name
MERCHANT_TYPE=Your_Business_Type
Note on Certificates: AZUL_CERT and AZUL_KEY can be provided as file paths. Alternatively, AZUL_CERT can be the full PEM content as a string, and AZUL_KEY can be the full PEM content as a string or a Base64 encoded PEM string. The library handles creating temporary files for PEM content internally.
Initializing PyAzul
Once your .env file is configured, initialize the PyAzul facade:
from pyazul import PyAzul
# The PyAzul class acts as a facade, providing a simplified interface to the SDK's features.
azul = PyAzul() # Automatically loads settings from .env and environment variables
# For custom settings (e.g., if not using a .env file or want to override):
# from pyazul.core.config import AzulSettings
# settings = AzulSettings(
# AUTH1="your_auth1",
# AUTH2="your_auth2",
# MERCHANT_ID="your_merchant_id",
# AZUL_CERT="-----BEGIN CERTIFICATE-----...", # Direct PEM content example
# AZUL_KEY="/path/to/your/private.key", # File path example
# ENVIRONMENT="dev"
# )
# azul = PyAzul(settings=settings)
Logging Configuration
By default, PyAzul minimizes log output in production. Most informational logs are set to DEBUG level. To enable detailed logging:
import logging
# To see debug messages from all of pyazul
logging.getLogger('pyazul').setLevel(logging.DEBUG)
# Or for a specific service, e.g., SecureService
logging.getLogger('pyazul.services.secure').setLevel(logging.DEBUG)
# Ensure you have a handler configured (e.g., for console output):
logging.basicConfig(level=logging.DEBUG) # Basic configuration
# If you have an existing logging setup, adjust your handlers as needed.
Core Functionality Examples
The following examples demonstrate common operations using the PyAzul facade. Ensure azul = PyAzul() has been initialized as shown above.
Process a Payment (Non-3DS)
from pyazul import PyAzul, AzulError
# from pyazul.models import SaleTransactionModel # Optional: use Pydantic model for request
# azul = PyAzul() # Initialization shown in "Quick Setup & Configuration"
async def process_payment():
payment_data = {
"Channel": "EC",
"PosInputMode": "E-Commerce",
"Amount": "100000",
"Itbis": "18000",
"CardNumber": "4111********1111", # Use a test card number
"Expiration": "202812", # YYYYMM
"CVC": "***",
"CustomOrderId": "order-001",
"OrderNumber": "INV-12345",
}
try:
response = await azul.sale(payment_data)
if response.get("ResponseMessage") == "APROBADA":
print(f"Payment approved: {response.get('AuthorizationCode')}, AzulOrderId: {response.get('AzulOrderId')}")
else:
print(f"Error: {response.get('ResponseMessage')}, Details: {response.get('ErrorDescription', response)}")
except AzulError as e:
print(f"An API error occurred: {e}")
Tokenize a Card
# from pyazul import PyAzul, AzulError # Assuming PyAzul and AzulError are already imported
# azul = PyAzul() # Initialization shown in "Quick Setup & Configuration"
async def tokenize_card():
token_creation_data = {
"CardNumber": "4111********1111",
"Expiration": "202812",
"Store": "your_merchant_id", # Ensure this matches your Merchant ID
}
try:
token_response = await azul.create_token(token_creation_data)
if token_response.get("ResponseMessage") == "APROBADA":
token_id = token_response.get("DataVaultToken")
print(f"Token created: {token_id}")
# Example: Use token for a non-3DS payment
token_payment_data = {
"Channel": "EC",
"DataVaultToken": token_id,
"Amount": "100000",
"Itbis": "000",
"OrderNumber": "TOKEN-ORD-XYZ"
}
payment_response = await azul.token_sale(token_payment_data)
if payment_response.get("ResponseMessage") == "APROBADA":
print(f"Token payment approved: {payment_response.get('AuthorizationCode')}")
else:
print(f"Token payment error: {payment_response.get('ResponseMessage')}")
else:
print(f"Token creation error: {token_response.get('ResponseMessage')}")
except AzulError as e:
print(f"An API error occurred: {e}")
Payment Page Integration
This example uses FastAPI to return the HTML for Azul's hosted payment page.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from pyazul import PyAzul # Assuming PyAzul is imported
# app = FastAPI() # Standard FastAPI app initialization
# azul = PyAzul() # Initialization shown in "Quick Setup & Configuration"
@app.get("/pay/{order_id}", response_class=HTMLResponse)
async def get_payment_page(order_id: str):
page_data = {
"Amount": "100000",
"ITBIS": "18000",
"OrderNumber": order_id,
"ApprovedUrl": "https://your-site.com/success",
"DeclineUrl": "https://your-site.com/declined",
"CancelUrl": "https://your-site.com/cancel",
}
html_form = azul.payment_page(page_data)
return HTMLResponse(content=html_form)
3D Secure Implementation Guide
PyAzul provides comprehensive support for 3D Secure 2.0. This section guides you through the multi-step 3DS flow. Ensure you have configured your credentials (AUTH1, AUTH2) in your .env file as outlined in the "Quick Setup & Configuration" section.
Overview of the 3DS Flow with PyAzul
The 3DS process involves interactions between the customer's browser, your server, the card issuer's Access Control Server (ACS), and the Azul gateway. PyAzul simplifies this by:
- Initiating the 3DS transaction and obtaining parameters from Azul.
- Providing helpers for redirecting the user if a challenge is required.
- Processing callbacks from the ACS (Method Notification and Challenge Completion).
- Finalizing the transaction with Azul.
A unique secure_id (an ID, typically a UUID string, generated by PyAzul) is created during the initiation of a 3DS transaction (e.g., via secure_sale). PyAzul automatically appends this secure_id as a query parameter (e.g., ?secure_id=<generated_id>) to the TermUrl and MethodNotificationUrl you provide. Your application must be prepared to extract this secure_id from the query string in your callback handlers to track the 3DS session.
Application-Side State Management (Crucial for 3DS)
PyAzul's SecureService uses an internal, in-memory dictionary (self.secure_sessions) to temporarily store some transaction data (like azul_order_id received from Azul and the modified term_url) associated with the secure_id. This internal store is primarily for the library's own operational needs during the 3DS flow and is not persistent across application restarts or multiple server instances.
For production environments, your application MUST implement its own robust session management solution (e.g., using Redis, a database, or your web framework's session handling). This application-level session store is critical for reliably managing state across the asynchronous callbacks of the 3DS flow. You should use the secure_id (obtained from the initial 3DS call's response, e.g., response.get("id"), and from callback query parameters) as the key to store and retrieve:
- Your internal order details or references.
- The
azul_order_idif it was returned in the initial response fromsecure_sale(it might be nested underresponse.get("value", {}).get("AzulOrderId")). - The original
TermUrlyou provided (needed forcreate_challenge_formif a challenge occurs after a method notification). - Any other application-specific data required to complete the order after the 3DS flow concludes.
PyAzul provides await azul.get_secure_session_info(secure_id) which allows you to inspect the limited data PyAzul stored internally for that secure_id (like azul_order_id and term_url). This can be useful as a fallback or for debugging but should not replace your application's primary session store.
The FastAPI example below uses a simple Python dictionary (app_level_session_store) for illustrative purposes of an application-level session. Do not use this in-memory dictionary for production.
Complete 3D Secure Flow (FastAPI Example)
This example demonstrates the 3DS flow using FastAPI.
from fastapi import FastAPI, Request, Form, Query
from fastapi.responses import HTMLResponse, JSONResponse
from pyazul import PyAzul, AzulError
# from pyazul.models import SecureSaleRequest # Optional: use Pydantic for request
app = FastAPI() # Initialize your FastAPI app
azul = PyAzul() # Initialize PyAzul (ensure 3DS settings are in .env)
# --- Application-Level Session Store (Example - REPLACE FOR PRODUCTION) ---
# This simple dictionary is for demonstration ONLY.
# Use a robust session management solution (Redis, database, etc.) in production.
app_level_session_store = {}
# --- End Example Session Store ---
# 1. Transaction Initiation Endpoint (Your Application)
@app.post("/initiate-3ds-payment")
async def initiate_3ds_payment(request_data: dict): # Process incoming payment data
# Store the original TermUrl from the request for potential use in create_challenge_form
# if a challenge arises after a 3DS method notification.
original_term_url = request_data.get("threeDSAuth", {}).get("TermUrl", "")
try:
# Initiate the 3DS process via PyAzul SDK
response = await azul.secure_sale(request_data)
secure_id = response.get("id") # PyAzul's unique ID for this 3DS session
# Attempt to get AzulOrderId if returned in the initial synchronous part of the response
# This might be nested under a 'value' key for direct approvals/declines.
azul_order_id_from_initial_response = None
if response.get("value") and isinstance(response.get("value"), dict):
azul_order_id_from_initial_response = response.get("value", {}).get("AzulOrderId")
if secure_id:
# Store essential data in YOUR application's session store for cross-callback access
app_session_data = {
"original_term_url": original_term_url, # Needed if challenge occurs after method step
"azul_order_id": azul_order_id_from_initial_response, # May be None initially
"internal_order_ref": request_data.get("OrderNumber"),
# Add other application-specific data as needed
}
app_level_session_store[secure_id] = app_session_data
# If AzulOrderId wasn't in the immediate response, try getting it from PyAzul's internal session
# This is particularly relevant if the first step is a redirect (method/challenge)
if not azul_order_id_from_initial_response:
pyazul_internal_session = await azul.get_secure_session_info(secure_id)
if pyazul_internal_session and pyazul_internal_session.get("azul_order_id"):
app_level_session_store[secure_id]["azul_order_id"] = pyazul_internal_session.get("azul_order_id")
print(f"Updated azul_order_id for {secure_id} from PyAzul session.")
if response.get("redirect"):
# HTML for immediate redirection (to 3DS Method URL or ACS Challenge)
return HTMLResponse(content=response.get("html"))
elif response.get("value", {}).get("ResponseMessage") == "APROBADA":
# Approved directly by Azul (e.g., frictionless flow without redirects)
return JSONResponse(content={"status": "approved_without_3ds_flow", "data": response.get("value")})
else:
# Other initial responses from Azul (e.g., declined before 3DS flow)
return JSONResponse(content={"status": "error_or_declined_early", "data": response.get("value")}, status_code=400)
except AzulError as e:
return JSONResponse(content={"status": "sdk_error", "message": str(e)}, status_code=500)
# 2. 3DS Method Notification Callback Endpoint (Your MethodNotificationUrl)
@app.post("/capture-3ds-method") # Path must match your MethodNotificationUrl in the initial request
async def capture_3ds_method_callback(secure_id: str = Query(...)): # secure_id is appended by PyAzul
try:
# Retrieve your application's session data using secure_id
app_session_data = app_level_session_store.get(secure_id)
if not app_session_data:
return JSONResponse(content={"error": "Application session not found for secure_id"}, status_code=404)
azul_order_id = app_session_data.get("azul_order_id")
original_term_url = app_session_data.get("original_term_url")
# azul_order_id is required by PyAzul's process_3ds_method.
# It should have been populated in the app_session_store by the /initiate-3ds-payment endpoint.
if not azul_order_id:
# As a fallback, try to get it from PyAzul's internal session again
pyazul_session = await azul.get_secure_session_info(secure_id)
if pyazul_session and pyazul_session.get("azul_order_id"):
azul_order_id = pyazul_session.get("azul_order_id")
app_level_session_store[secure_id]["azul_order_id"] = azul_order_id # Update your app session
else:
return JSONResponse(content={"error": "Azul Order ID not found or retrievable for secure_id"}, status_code=400)
if not original_term_url:
return JSONResponse(content={"error": "Original TermUrl not found in app session. This is needed for potential challenge forms."}, status_code=400)
# Notify PyAzul that the 3DS Method information was processed by the cardholder's bank
result = await azul.process_3ds_method( # Corrected method name
azul_order_id=azul_order_id,
method_notification_status="RECEIVED" # Standard status for successful method notification
)
if result.get("ResponseMessage") == "3D_SECURE_CHALLENGE":
# Challenge is required; generate HTML form to redirect user to the ACS
form_html = azul.create_challenge_form(
result["ThreeDSChallenge"]["CReq"], # Challenge Request from Azul
# Use the TermUrl from your app session, which PyAzul would have already appended secure_id to.
# If PyAzul's internal term_url is preferred, get it from azul.get_secure_session_info(secure_id)
app_level_session_store[secure_id].get("original_term_url") + f"?secure_id={secure_id}",
result["ThreeDSChallenge"]["RedirectPostUrl"] # ACS URL from Azul
)
return HTMLResponse(content=form_html)
elif result.get("ResponseMessage") == "APROBADA":
# Approved after 3DS Method (e.g., frictionless flow, no challenge was needed)
return JSONResponse(content={"status": "approved_after_method", "data": result})
else:
# Other outcomes (e.g., RECHAZADA, or still pending challenge if ACS flow differs)
return JSONResponse(content={"status": "pending_or_error_after_method", "data": result})
except AzulError as e:
return JSONResponse(content={"status": "sdk_error", "message": str(e)}, status_code=500)
# 3. 3DS Challenge Completion Callback Endpoint (Your TermUrl)
@app.post("/post-3ds-callback") # Path must match your TermUrl in the initial request
async def post_3ds_challenge_callback(
secure_id: str = Query(...), # secure_id is appended by PyAzul to TermUrl
CRes: str = Form(...) # CRes (Challenge Response) POSTed by ACS
):
try:
# Process the challenge response via PyAzul SDK
final_result = await azul.process_challenge(session_id=secure_id, challenge_response=CRes) # Corrected parameter name
if final_result.get("ResponseMessage") == "APROBADA":
return JSONResponse(content={"status": "approved_after_challenge", "data": final_result})
else:
# Declined or error after challenge completion
return JSONResponse(content={"status": "declined_or_error_after_challenge", "data": final_result}, status_code=400)
except AzulError as e:
return JSONResponse(content={"status": "sdk_error", "message": str(e)}, status_code=500)
Key Considerations for 3DS
- Callback URLs: Ensure your
TermUrlandMethodNotificationUrl(provided in the initialsecure_salecall) correctly point to your server endpoints that can handle these callbacks and extract thesecure_idquery parameter. CardHolderInfoandThreeDSAuth: Provide as much accurate data as possible in these objects during thesecure_saleinitiation to improve the chances of a frictionless flow and successful authentication.- Error Handling: Implement robust error handling for each step, including checking responses from PyAzul and handling potential
AzulErrorexceptions.
Request Data and Pydantic Models
PyAzul methods, accessed via the PyAzul facade, primarily accept Python dictionaries for request data. For enhanced type safety and autocompletion in your development environment, you can use PyAzul's Pydantic models (found in pyazul.models).
When using Pydantic models, you construct the model instance and then pass model_instance.model_dump(exclude_none=True) to the PyAzul method.
Data Formatting (e.g., Amount, ITBIS)
The SDK's internal Pydantic models automatically handle the formatting of fields like Amount and ITBIS. You can provide these as:
- Integers (representing cents, e.g.,
100000for $1,000.00) - Floats (representing cents, e.g.,
100000.00) - Pre-formatted strings of cents (e.g.,
"100000") The SDK ensures they are converted to the string format required by the external Azul API.
from pyazul.models import SaleTransactionModel # Import specific model
# Example: Using a dictionary (common approach)
payment_dict = {
"CardNumber": "4111...", "Expiration": "202812", "CVC": "123",
"Amount": 50000, # 500.00 (as integer cents)
"Itbis": 0, # 0.00 (as integer cents)
"OrderNumber": "MYORDER001", "Store": "your_merchant_id"
}
# response = await azul.sale(payment_dict)
# Example: Using a Pydantic model
data_model = SaleTransactionModel(
CardNumber="4111...", Expiration="202812", CVC="123",
Amount=50000, # 500.00
Itbis=0,
OrderNumber="MYORDER001", Store="your_merchant_id"
)
# response = await azul.sale(data_model.model_dump(exclude_none=True))
Refer to pyazul/models/schemas.py and pyazul/models/secure.py for detailed field definitions.
Example Data Structures (Dictionaries)
Below are common request structures as Python dictionaries.
Basic Transaction Models
# Card Sale Transaction
{
"CardNumber": "4111111111111111", "Expiration": "202812", "CVC": "123",
"Amount": "100000", # Or integer 100000
"Itbis": "18000", # Or integer 18000
"OrderNumber": "INV-12345",
"CustomOrderId": "order-123", # Optional
"SaveToDataVault": "2" # Optional: "1" to save, "2" (default) not to save
# "Store": "your_merchant_id" # Required if not matching default in settings
}
# Other non-3DS models (TokenSale, Hold, PostSale, Void, Refund, Verify)
# generally involve similar fields or specific identifiers like AzulOrderId or DataVaultToken.
# Consult model files for specifics.
3D Secure Models
Essential components for a secure_sale request:
# Cardholder Information (part of cardHolderInfo object)
{
"BillingAddressCity": "Santo Domingo", "BillingAddressCountry": "DO",
"BillingAddressLine1": "Main Street #123", "BillingAddressState": "Distrito Nacional",
"BillingAddressZip": "10101", "Email": "customer@example.com", "Name": "John Doe",
# Other fields like Phone1, ShippingAddress, etc., are available.
}
# 3DS Authentication Parameters (part of threeDSAuth object)
{
"TermUrl": "https://your-site.com/3ds-complete-callback", # PyAzul appends ?secure_id=...
"MethodNotificationUrl": "https://your-site.com/3ds-method-callback", # PyAzul appends ?secure_id=...
"RequestChallengeIndicator": "03" # e.g., "01": No preference, "03": Prefer challenge
}
# Full 3DS Secure Sale Request (Example for azul.secure_sale)
{
"CardNumber": "4111111111111111", "Expiration": "202812", "CVC": "123",
"Amount": 100000, # Integer cents
"Itbis": 18000, # Integer cents
"OrderNumber": "3DS-ORDER-123",
"Store": "your_merchant_id", # Required
"forceNo3DS": "0", # "0" to enable 3DS, "1" to attempt non-3DS
"cardHolderInfo": {
"Name": "John Doe", "Email": "john@example.com",
"BillingAddressCity": "Santo Domingo", "BillingAddressCountry": "DO",
"BillingAddressLine1": "Main St #123", "BillingAddressState": "Distrito Nacional",
"BillingAddressZip": "10101"
# ... other cardHolderInfo fields
},
"threeDSAuth": {
"TermUrl": "https://your-domain.com/post-3ds-callback",
"MethodNotificationUrl": "https://your-domain.com/capture-3ds-method",
"RequestChallengeIndicator": "03"
# ... other threeDSAuth fields
}
}
# SecureTokenSale and SecureHold follow similar patterns, using DataVaultToken instead of full card details for SecureTokenSale.
API Reference
The PyAzul object is your main entry point and acts as a facade to the SDK's capabilities. Key methods include:
# azul = PyAzul() # Assumed initialized
# --- Non-3DS Transaction processing ---
# await azul.sale(data_dict_or_model_dump)
# await azul.token_sale(data_dict_or_model_dump)
# await azul.hold(data_dict_or_model_dump)
# await azul.post_sale(data_dict_or_model_dump) # Capture an existing hold
# await azul.void(data_dict_or_model_dump)
# await azul.refund(data_dict_or_model_dump)
# await azul.verify(data_dict_or_model_dump) # Verify transaction status
# --- Card Tokenization (DataVault) ---
# await azul.create_token(data_dict_or_model_dump)
# await azul.delete_token(data_dict_or_model_dump)
# --- Payment Page ---
# html_form_string = azul.payment_page(data_dict_or_model_dump)
# --- 3D Secure ---
# response_dict = await azul.secure_sale(data_dict_or_model_dump)
# response_dict = await azul.secure_token_sale(data_dict_or_model_dump)
# response_dict = await azul.secure_hold(data_dict_or_model_dump)
# --- 3DS Callback Helpers ---
# response_dict = await azul.process_3ds_method(azul_order_id="...", method_notification_status="...")
# response_dict = await azul.process_challenge(session_id="...", challenge_response="...")
# html_form_string = azul.create_challenge_form(creq="...", term_url="...", redirect_post_url="...")
# --- 3DS Session Inspection (for debugging) ---
# session_data_dict = await azul.get_secure_session_info(session_id="...")
# Access to underlying components (generally not needed for typical use):
# azul.transaction # Instance of TransactionService
# azul.datavault # Instance of DataVaultService
# azul.payment_page # Instance of PaymentPageService
# azul.secure # Instance of SecureService
# azul.api # Instance of AzulAPI (the HTTP client for external Azul API)
# azul.settings # The active AzulSettings instance
Error Handling
The PyAzul SDK uses a hierarchy of custom exceptions (all inheriting from pyazul.AzulError) to report issues:
pyazul.core.exceptions.AzulError: Base exception for the library.pyazul.core.exceptions.SSLError: For SSL certificate loading or configuration problems.pyazul.core.exceptions.APIError: For general HTTP client issues or problems communicating with the external Azul API (e.g., network errors, unexpected responses not covered byAzulResponseError).pyazul.core.exceptions.AzulResponseError: Raised when the external Azul API explicitly returns an error in its response payload (e.g., transaction declined, invalid field).- This exception has a
response_dataattribute containing the raw dictionary response from Azul for inspection. - The error message usually includes
ErrorMessageorErrorDescriptionfrom the Azul response.
- This exception has a
Always wrap calls to PyAzul SDK methods in try...except blocks:
from pyazul import AzulError # Or from pyazul.core.exceptions import AzulError, AzulResponseError, etc.
try:
# response = await azul.sale({...})
pass # Replace with your PyAzul SDK call
except AzulResponseError as e:
print(f"The external Azul API returned an error: {e.message}")
print(f"Azul Response Data: {e.response_data}")
except APIError as e:
print(f"API communication error: {e}")
except SSLError as e:
print(f"SSL configuration error: {e}")
except AzulError as e: # Catch-all for other PyAzul SDK specific errors
print(f"An error occurred within the PyAzul SDK: {e}")
except Exception as e: # Catch any other unexpected errors
print(f"An unexpected error occurred: {e}")
More Information about Azul
For complete official documentation on the external Azul API, services, and field specifications, refer to the Azul Developer Portal. (This link is subject to change; please verify on Azul's official website if it becomes outdated).
© MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyazul-2.1.2.tar.gz.
File metadata
- Download URL: pyazul-2.1.2.tar.gz
- Upload date:
- Size: 57.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d59d7c5d498f531b3251fc074590738f9740ce0400ae26c039a100fd3cd6af94
|
|
| MD5 |
5c2a54bbeae981ca9b6734b866d0375e
|
|
| BLAKE2b-256 |
b332ed6153813987ad9efbb4046437f46f20ecc1f22b2368427defe3ef981782
|
Provenance
The following attestation bundles were made for pyazul-2.1.2.tar.gz:
Publisher:
cd.yaml on indexa-git/pyazul
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyazul-2.1.2.tar.gz -
Subject digest:
d59d7c5d498f531b3251fc074590738f9740ce0400ae26c039a100fd3cd6af94 - Sigstore transparency entry: 223905652
- Sigstore integration time:
-
Permalink:
indexa-git/pyazul@a1ec975e52b6b516a822e5f82a449b827cca9dae -
Branch / Tag:
refs/tags/2.1.2 - Owner: https://github.com/indexa-git
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yaml@a1ec975e52b6b516a822e5f82a449b827cca9dae -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyazul-2.1.2-py3-none-any.whl.
File metadata
- Download URL: pyazul-2.1.2-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
577a80c9913c14f7846e632f22a8920413aee8787848a79c7c52b17213a42cf3
|
|
| MD5 |
0dff88093e5efa5bc5353c137994bb61
|
|
| BLAKE2b-256 |
b708c27864f76eb51fb628f135b7a729e2c29108173eb9d7b5aee356ce201db1
|
Provenance
The following attestation bundles were made for pyazul-2.1.2-py3-none-any.whl:
Publisher:
cd.yaml on indexa-git/pyazul
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyazul-2.1.2-py3-none-any.whl -
Subject digest:
577a80c9913c14f7846e632f22a8920413aee8787848a79c7c52b17213a42cf3 - Sigstore transparency entry: 223905655
- Sigstore integration time:
-
Permalink:
indexa-git/pyazul@a1ec975e52b6b516a822e5f82a449b827cca9dae -
Branch / Tag:
refs/tags/2.1.2 - Owner: https://github.com/indexa-git
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yaml@a1ec975e52b6b516a822e5f82a449b827cca9dae -
Trigger Event:
release
-
Statement type: