Ingenico Direct Support Site

Results for

icon-search-large No search results yet
Enter your search query above

Introduction

To understand how to use this SDK it is best to read the following documentation:

  • Server Introduction
    First read the Server Introduction to familiarize yourself with the various concepts.
  • Server API Reference
    This Server SDK wraps the Server API and (amongst other things) exposes the responses of the webservice calls as Python objects. Understanding the Server API will help you understanding these SDK objects as well.
  • This current document will help you understand the global flow when interacting with the Ingenico Direct platform using the Python SDK.

The Python SDK helps you to communicate with the Server API. More specifically, it offers a fluent Python API that provides access to all the functionality of the RESTful Server API. Below, we discuss the following topics in detail.

The source code of the SDK is available on Github for Python 3 and Python 2. There you can find installation instructions.

The API documentation of the latest version of the SDK is available for Python 3 and Python 2. For a specific version, replace "latest" with the actual version.

Initialization of the Python SDK

All Python code snippets presented in the API reference assume you have initialized the Python SDK before using them in your Development Environment. This section details the initialization of the Python SDK.

Initializing is simple and requires only one key task: use our Factory class to create an instance of IClient, which contains the actual methods to communicate with the Server API.

The Factory needs the following input information to provide you with an initialized IClient

  • A String to the configuration file with your connection configuration.
  • The secret_api_key and api_key_id. The secret_api_key is a key that is used to authenticate your API requests, and api_key_id identifies that key (as you can have multiple active keys). Both of these can be obtained from the Back Office (Configuration > Technical Information > Ingenico Direct settings), and are available only if you are administrator.

The configuration file should contain the following keys:

SDK: Python
[DirectSDK]
direct.api.integrator=<your company name>
direct.api.endpoint.host=api.domain.com
; The following keys are optional and use the given value by default.
direct.api..endpoint.scheme=https
direct.api..endpoint.port=-1
direct.api.connectTimeout=5
direct.api.socketTimeout=300
direct.api.maxConnections=10
direct.api.authorizationType=v1HMAC

See API endpoints for the possible hosts.

If a proxy should be used, the configuration file should additionally contain the following key(s) under the [DirectSDK] section:

SDK: Python
[DirectSDK]
direct.api.proxy.uri=<URL to the proxy host including leading http(s)://> # omit the following two lines if no proxy authentication is required direct.api.proxy.username=<username for the proxy> direct.api.proxy.password=<password for the proxy>

You can create an instance of IClient using the Factory with this code snippet:

SDK: Python
client = Factory.create_client_from_file(configuration_file_name, "api_key_id", "secret_api_key")

This IClient instance offers connection pooling and can be reused for multiple concurrent API calls. Once it is no longer used it should be closed.

IClient defines __enter__ and __exit__ methods, which allows it to be used in with statements.

Connection management

Connection pooling is configured with the max_connections setting in CommunicatorConfiguration or in the configuration file. Its value determines how many connections to the Server API are kept alive for re-use in a connection pool. If there are more requests in progress than this maximum number of connections, then a new connection will be temporarily opened.

Client meta information

Optionally, for BI and fraud prevention purposes, you can supply meta information about the client used by the customer. To do so, create a new instance of IClient at the start of the customer's payment process as follows:

SDK: Python
consumer_specific_client = client.with_client_meta_info("consumer specific JSON meta info")

This consumer-specific instance will use the same connection pool as the IClient from which it was created. As a result, closing an IClient instance will close all IClient instances created using the with_client_meta_info method. There is no need to close them separately.

This closing works both ways. If an IClient created using the with_client_meta_info method is closed this will also close the IClient it originated from. This will in turn close all other IClient instances created using the with_client_meta_info method. This can be used if only a IClient with client meta info is needed.

Do not use this consumer specific instance for API calls for other consumers.

Example JSON meta information for a mobile app client:

SDK: Python
X-GCS-ClientMetaInfo: {
    "platformIdentifier": "Android/4.4",
    "appIdentifier": "Example mobile app/1.1",
    "sdkIdentifier": "AndroidClientSDK/v1.2",
    "screenSize": "800x600",
    "deviceBrand": "Samsung",
    "deviceType": "GT9300",
    "ipAddress": "123.123.123.123"
}

Example JSON meta information for the JavaScript SDK running in a browser:

SDK: Python
X-GCS-ClientMetaInfo: {
    "platformIdentifier": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
    "sdkIdentifier": "JavaScriptClientSDK/v1.2",
    "screenSize": "800x600"
}

Payments

As a merchant, your core interaction with Ingenico Direct typically starts when your customer clicks the checkout button in your application. The payment process usually has the following steps:

  1. Payment Product selection
  2. Setting of available information needed for selected payment product (e.g. amount of the order)
  3. Collection of missing customer information needed for selected payment product (e.g. creditcard number)
  4. Submitting the payment request to the Ingenico Direct platform
  5. Handling the response to the payment request (e.g. payment requested)

The Ingenico Direct platform offers different ways of handling this payment process:

  • Use a hosted payment through the hosted payment pages.
    In this case, you redirect the customer to our hosted payment pages. For you as a merchant, this is the easiest option as the Ingenico Direct platform can handle payment product selection and is responsible for the collection of sensitive data like a credit card number.
  • Use a Server SDK to build a payment flow hosted on your server.
    In this case, you can use the Server SDK to obtain the payment products that are applicable to the payment, to obtain the fields that need to be collected from the customer for a selected payment product, and to submit the payment request itself.

In the next couple of paragraphs, we discuss each of these options in more detail.

Use a hosted payment through the Hosted Payment Page

The high-level flow of a hosted payment is described below, followed by a more detailed look at each of the steps.

  1. At this point, your customer has provided all relevant information regarding the order, e.g. a shopping cart of items and a shipping address.
  2. See the section on initialization. Use Factory to create an instance of IClient if you hadn't done so yet and set the meta data that you've collected about the client of the customer.
  3. Create a CreateHostedCheckoutRequest body and populate at least its order. See the relevant section of the full API reference for more details. You can specify an optional return_url, which is used to redirect your customer back to your website in step 9.
  4. The create hosted checkout SDK call returns a CreateHostedCheckoutResponse response. Store the hosted_checkout_id and RETURNMAC it contains, as well as any other relevant order information. You will need these when your customer returns from our hosted payment pages, or when the customer fails to return in a reasonable amount of time. Now take response.partial_redirect_url and prepend "https://payment." to it to create hosted_checkout_url
  5. After completing the interactive payment process in the Hosted Payment Page, your customer is redirected back to the url you provided in step 3 as body.hosted_checkout_specific_input.return_url. The hosted_checkout_id and RETURNMAC you stored in step 5 are added to this URL as query parameters. Specifying a return_url is optional, however. As a result, your customer is only redirected back if you've provided a URL in step 3.
  6. If you cannot identify the customer based on e.g. the HTTP session, you can use the hosted_checkout_id for this purpose. If you do, you must check that the hosted_checkout_id and RETURNMAC from the return_url match those that you stored in step 3. Note that the RETURNMAC is used as a shared secret between the Ingenico Direct platform and your system that is specific for this hosted checkout.
  7. Retrieve the results of the customer's interaction with the Ingenico Direct platform.
  8. Check the GetHostedCheckoutResponse response returned in step 13. If response.status equals PAYMENT_CREATED, then the customer attempted a payment, the details of which can be found in response.created_payment_output. Depending on the payment product chosen and the status of the payment you can "deliver the goods" immediately or set up a regular poll of the created payment to wait for the status. Such a poll is done using the SDK call client.merchant("merchant_id").payments().get_payment(payment_id), where payment_id is response.created_payment_output.payment.id. For details on the various payment products and their statuses, see Payment Products.

Additionally, it may be the case that the customer does not return in time (or at all), for example because the browser is closed or because you didn't provide a return_url. In this case, you need to retrieve the status of the hosted checkout (step 12) before the hosted checkout times out, which happens after 2 hours, and follow step 14 from the image as well.

Use a Server SDK to build a payment flow hosted on your server

The high-level flow of a payment performed from pages hosted on your server is described below, followed by a more detailed look at each of the steps. First, we describe the flow for payment products that do not require a redirect to a payment method hosted by a third party. Afterwards, the flow for payment methods that require a redirect is described.

  1. At this point, your customer has provided all relevant information regarding the order, e.g. a shopping cart of items and a shipping address.
  2. See the section on initialization. Use Factory to create an instance of IClient if you hadn't done so yet, and set the meta data that you've collected about the client of the customer.
  3. Create GetPaymentProductsParams query params and request a list of relevant payment products. See the relevant section of the full API reference for more details.
  4. Show the relevant payment products to your customers such that they can select one.
  5. The customer selects one of the available the payment products.
  6. Once the customer has decided which payment product should be used, you request the fields of this payment product. See the relevant section of the full API reference for more details.
  7. Based on the information retrieved in the previous step, you render a form that the customer can use to enter all relevant information for the selected payment product.
  8. The customer submits the form.
  9. Create a CreatePaymentRequest body, populate its order and other properties depending on the selected payment product, and submit it. See the relevant section of the full API reference for more details. Do not store the information provided by the customer. The payment_product_id can be used to determine whether this payment involves a redirect to a third party. For this flow, we assume that we're dealing with a payment that doesn't require a redirect.
  10. The create payment SDK call returns a CreatePaymentResponse response. The status of the payment is accessible via response.payment.status. Depending on the payment product chosen and the status of the payment you can "deliver the goods" immediately, or set up a regular poll of the created payment to wait for the status. Such a poll is done using the SDK call client.merchant("merchantId").payments().get_payment(payment_id), where paymentId is response.payment.id. For details on the various payment products and their statuses, see Payment Products.

The high-level flow of a payment performed from pages on your server is a little different if a redirect is involved. We only describe the steps that differ from the flow without a redirect.

  1. We assume that we're dealing with a payment that involves a redirect. As mentioned above, this can be determined using the payment_product_id. Create a CreatePaymentRequest body and populate at least its order. Additionally, populate its redirect_payment_method_specificInput by providing at least the desired return_url. The return_url defines the location to which the customer should be redirected after completing the payment process. See the relevant section of the full API reference for more details.
  2. The create payment SDK call returns a CreatePaymentResponse response. For payments involving a redirect, response.merchant_action.redirect_data.redirect_url defines the URL to which the customer should be redirected to complete the payment. You need to store the value ofresponse.merchant_action.redirect_data.returnmac because it should be compared with the RETURNMAC returned by the third party at a later stage. Additionally, you need to store the value of response.payment.id. This payment_id is needed to check the status of the payment after the redirect.
  3. Redirect the customer to the redirectUrl.
  4. After the payment process is completed, your customer is redirected to the return_url specified previously. In the flow shown in the figure above, we assume that this URL brings the customer back to your server.
  5. Retrieve the RETURNMAC provided by the third party from the return_url. You can use the RETURNMAC to identify the customer by comparing it with the one stored previously, and to validate that the customer was redirected to you by our systems.
  6. Use the payment_id stored previously to check the status of the payment. See the relevant section of the full API reference for more details. The retrieve payment SDK call returns a PaymentResponse response. The status of the payment is accessible via response.payment.status. Use this status to handle the order appropriately, as described above.

Idempotent requests

To execute a request as an idempotent request, you can call the same method as for a non-idempotent request, but with an extra CallContext argument with its idempotence_key property set. This will make sure the SDK will send an X-GCS-Idempotence-Key header with the idempotence key as its value.

If a subsequent request is sent with the same idempotence key, the response will contain an X-GCS-Idempotence-Request-Timestamp header, and the SDK will set the idempotence_request_timestamp property of the CallContext argument. If the first request has not finished yet, the RESTful Server API will return a 409 status code. If this occurs, the SDK will raise an IdempotenceException with the original idempotence key and the idempotence request timestamp.
For example:

SDK: Python
context = CallContext(idempotence_key)
try:
    reponse = client.merchants(merchant_id).payments().create_payment(request, context)
except IdempotenceException as e:
    pass
    # a request with the same idempotence_key is still in progress, try again after a short pause
    # e.idempotence_request_timestamp contains the value of the
    # X-GCS-Idempotence-Request-Timestamp header
finally:
    idempotence_request_timestamp = context.idempotence_request_timestamp
    # idempotence_request_timestamp contains the value of the
    # X-GCS-Idempotence-Request-Timestamp header
    # if idempotence_request_timestamp is not None this was not the first request
If an idempotence key is sent for a call that does not support idempotence, the RESTful Server API will ignore the key and treat the request as a first request.

Exceptions

Payment exceptions

If a payment attempt is declined by the RESTful Server API, a DeclinedPaymentException is raised. This exception contains a reference to the payment result which can be inspected to find the reason why the payment attempt was declined. This payment result can also be used to later retrieve the payment attempt again.
For example:

SDK: Python
try:
    response = client.merchants(merchant_id).payments().create_payment(request)
except DeclinedPaymentException as e:
    payment = e.create_payment_result.payment
    payment_id = payment.id
    payment_status = payment.status
    sys.stderr.write("Payment " + payment_id + " was declined with status " + payment_status)

Unlike direct payments, indirect payments like iDeal and PayPal usually will not cause a DeclinedPaymentException to be raised, but instead will result in a CreatePaymentResponse return value. To determine if the payment was successfully finished, declined or cancelled, you would need to retrieve the payment status and examine its contents, especially the status field. It is recommended to use shared code for handling errors.
For example:

SDK: Python
payment_id = None
try:
    response = client.merchants(merchant_id).payments().create_payment(request)
    payment_id = respons.payment.id
except DeclinedPaymentException as e:
    payment = e.create_payment_result.payment
    self.handle_payment_error(payment)
    return
# other code
payment = client.merchants(merchant_id).payments().get_payment(payment_id)
if is_not_successful(payment):
    self.handle_payment_error(payment)

Refund exceptions

If a refund attempt is declined by the RESTful Server API, a DeclinedRefundException is raised. This exception contains a reference to the refund result which can be inspected to find the reason why the refund was declined. This refund result can also be used to later retrieve the refund attempt again.
For example:

SDK: Python
try:
    response = client.merchants(merchant_id).payments().refund_payment(payment_id, request)
except DeclinedRefundException as e:
    refund = e.refund_result
    refund_id = refund.id
    refund _status = refund.status
    sys.stderr.write("Refund " + refund_id + " was declined with status " + refund_status)

Other exceptions

Besides from the above exceptions, all calls can raise one of the following runtime exceptions:

  • A ValidationException if the request was not correct and couldn't be processed (HTTP status code 400)
  • An AuthorizationException if the request was not allowed (HTTP status code 403)
  • An IdempotenceException if an idempotent request caused a conflict (HTTP status code 409)
  • A ReferenceException if an object was attempted to be referenced that doesn't exist or has been removed, or there was a conflict (HTTP status code 404, 409 or 410)
  • A DirectException if something went wrong on the Ingenico Direct platform. The Ingenico Direct platform was unable to process a message from a downstream partner/acquirer, or the service that you're trying to reach is temporary unavailable (HTTP status code 500, 502 or 503)
  • An ApiException if the RESTful Server API returned any other error

A payment attempt can now be handled as follows:

SDK: Python
try:
    response = client.merchants(merchant_id).payments().create_payment(request)
except DeclinedPaymentException as e:
    payment = e.create_payment_result().payment
    payment_id = payment.id
    payment_status = payment.status
    sys.stderr.write("Payment " + payment_id + " was declined with status " + payment_status)
except ValidationException as e:
    sys.stderr.write("Input validation error:")
    for error in e.errors:
        if error.property_name is None:
            sys.stderr.write("- " + error.code + ": " + error.message)
        else:
            sys.stderr.write("- " + error.property_name + ": " + error.code + ": " + error.message)
except AuthorizationException as e:
    sys.stderr.write("Authorization error:")
    for error in e.errors:
        sys.stderr.write("- " + error.code + ": " + error.message)
except ReferenceException as e:
    sys.stderr.write("Incorrect object reference:")
    for error in e.errors:
        sys.stderr.write("- " + error.code + ": " + error.message)
except DirectException as e:
    sys.stderr.write("Error occurred at Ingenico Direct or a downstream partner/acquirer:")
    for error in e.errors:
        sys.stderr.write("- " + error.code + ": " + error.message)
except ApiException as e:
    sys.stderr.write("Ingenico Direct error:")
    for error in e.errors:
        sys.stderr.write("- " + error.code + ": " + error.message)

Exception overview

The following table is a summary that shows when each of these exceptions will be raised:

HTTP status code Meaning Description Exception Type
200 Successful Your request was processed correctly N/A
201 Created Your request was processed correctly and a new resource was created.
The URI of this created resource is returned in the Location header of the response.
N/A
204 No Content Your request was processed correctly N/A
various; CreatePaymentResult is present in the response Payment Rejected Your request was rejected either by the Ingenico Direct platform or one of our downstream partners/acquirers. DeclinedPaymentException
various; RefundResult is present in the response Refund Rejected Your request was rejected either by the Ingenico Direct platform or one of our downstream partners/acquirers. DeclinedRefundException
400 Bad Request Your request is not correct and can't be processed. Please correct the mistake and try again. ValidationException
403 Not Authorized You're trying to do something that is not allowed or that you're not authorized to do. AuthorizationException
404 Not Found The object you were trying to access could not be found on the server. ReferenceException
409 Conflict Your idempotent request resulted in a conflict. The first request has not finished yet. IdempotenceException
409 Conflict Your request resulted in a conflict. Either you submitted a duplicate request or you're trying to create something with a duplicate key. ReferenceException
410 Gone The object that you are trying to reach has been removed. ReferenceException
500 Internal Server Error Something went wrong on the Ingenico Direct platform. DirecttException
502 Bad Gateway The Ingenico Direct platform was unable to process a message from a downstream partner/acquirer. DirectException
503 Service Unavailable The service that you're trying to reach is temporary unavailable.
Please try again later.
DirectException
other Unexpected error An unexpected error has occurred ApiException

Logging

The Python SDK supports logging of requests, responses and exceptions of the API communication.

In order to start using the logging feature, an implementation of the CommunicatorLogger interface should be provided. The SDK provides two example implementations for logging to sys.stdout (SysOutCommunicatorLogger) and logging to a logger (PythonCommunicatorLogger).

Logging can be enabled by calling the enable_logging method on a IClient object, and providing the logger as an argument. The logger can subsequently be disabled by calling the disable_logging method.

When logged messages contain sensitive data, this data is obfuscated.

The following code exemplifies the use of adding a logger:

SDK: Python
client = Factory.create_client_from_file(configuration_file_name, "api_key_id", "secret_api_key")
logger = PythonCommunicatorLogger(logging.get_logger(...), 800)
client.enable_logging(logger)
# ... Do some calls
client.disable_logging()

Advanced use: Connection pooling

An IClient created using the Factory class from a properties file or CommunicatorConfiguration object will use its own connection pool. If multiple clients should share a single connection pool, the Factory class should be used to first create a shared Communicator, then to create IClient instances that use that Communicator:

SDK: Python
communicator = Factory.create_communicator(configuration_file_name, "api_key_id", "secret_api_key")
client = Factory.create_client_from_communicator(communicator)

Instead of closing these IClient instances, you should instead close the Communicator when it is no longer needed. This will close all IClient instances that use the Communicator.

If instead one of the IClient instances is closed, the Communicator will be closed as well. As a result, all other IClient instances that use the Communicator will also be closed. Attempting to use a closed IClient or Communicator will result in an error.

Just like IClient, Communicator defines __enter__ and __exit__ methods, and can therefore also be used in with statements.

Connection management

Just like IClient, Communicator also has method close_expired_connections that can be used to evict expired HTTP connections. You can call this method on the Communicator instead of on any of the IClient instances. The effect will be the same.

Advanced use: Customization of the communication

An IClient uses a Communicator to communicate with the RESTful Server API. A Communicator contains all the logic to transform a request object to a HTTP request and a HTTP response to a response object. If needed, you can extend this class. To instantiate a IClient that uses your own implementation of Communicator you can use the following code snippet:

SDK: Python
communicator = YourCommunicator()
client = Factory.create_client_from_communicator(communicator)

However, for most customizations you do not have to extend Communicator. The functionality of the Communicator is built on classes Authenticater, Connection, Marshaller and MetaDataProvider, the implementation of which can easily be extended or replaced. Marshaller is used to marshal and unmarshal request and response objects to and from JSON. It is unlikely that you will want to change this module. The other modules are needed to communicate with the Ingenico Direct platform:

  • Connection represents one or more HTTP connections to the Ingenico Direct server.
  • Authenticator is used to sign your requests.
  • MetaDataProvider constructs the header with meta data of your server that is sent in requests for BI and fraud prevention purposes.

For your convenience, the Factory.create_communicator_from_configuration and Factory.create_communicator_from_file functions can take optional arguments to set the Connection, Authenticator, Marshaller or MetaDataProvider modules. For example, the following code snippet allows you to use your own Connection implementation:

SDK: Python
connection = YourConnection()
communicator = Factory.create_communicator_from_file(configuration_file_name,
                                           api_key_id = "api_key_id",
                                           secret_api_key = "secret_api_key",
                                           connection = connection)
client = Factory.create_client_from_communicator(communicator)

Connection management

Calling close_expired_connections on an IClient or a Communicator object only works if the Connection extends PooledConnection, otherwise these methods do nothing. If you write a custom Connection that uses a pool of HTTP connections, extend PooledConnection instead.

Logging

To facilitate implementing logging in a custom Connection, the SDK provides utility classes RequestLogMessage and ResponseLogMessage. These can be used to easily construct request and response messages. For instance:

SDK: Python
# In the below code, logger is the CommunicatorLogger set using enable_logging.
# Note that it may be None if enable_logging is not called.
request_id = uuid.uuid4()
request_log_message = RequestLogMessage(request_id, method, uri)
# add request headers to request_log_message
# if present, set the request body on request_log_message
logger.log(request_log_message.get_message())
start_time = time.time() * 1000
# send the request
end_time = time.time() * 1000
duration = end_time - start_time
status_code = ...
# note: duration is optional
response_log_message = ResponseLogMessage(request_id, status_code, duration)
# add response headers to response_log_message
# if present, set the response body on response_log_message
logger.log(response_log_message.get_message())

Notes

Renaming of properties

The Python SDK uses Python naming conventions. As a result, properties have been turned into so-called Snake case . For example, hostedCheckoutSpecificInput is called hosted_checkout_specific_input, and RETURNMAC is called returnmac.