NAME

Business::cXML - Perl implementation of cXML messaging

SYNOPSIS

Respond to an incoming request:

use Business::cXML;

$cxml = new Business::cXML (
	local    => 'https://example.com/our/cxml',
	handlers => {
		PunchOutSetup => {
			__handler        => sub { ... },
			operationAllowed => 'create',
		},
	},
);
# Calls $res->reply_to($req), so to/from/sender are probably OK.
$output_xml_string = $cxml->process($input_xml_string);
# Send output to requestor...

Send a request to a server:

use Business::cXML;

$cxml = new Business::cXML (
	remote => 'https://example.com/rest/cxml',
	secret => 'somesecrettoken',
);
$req = $cxml->new_request('PunchOutSetup');
$req->from(id => '123456', domain => 'DUNS');  # Also sets Sender by default
$req->to(id => '654321', domain => 'NetworkId');
# Populate request, see Business::cXML::Transmission documentation...
$res = $cxml->send($req);
# Do something with $res, the Business::cXML::Transmission response received...

Create a one-way message:

use Business::cXML;

$cxml = new Business::cXML;
$msg = $cxml->new_message('PunchOutOrder');
$msg->from(id => '123456', domain => 'DUNS');  # Also sets Sender by default
$msg->to(id => '654321', domain => 'NetworkId');
# Populate message, see Business::cXML::Transmission documentation...
print $cxml->stringify($msg, url => '...');  # Transmission in cXML-Base64 in an HTML FORM

DESCRIPTION

Dispatch incoming HTTP/HTTPS requests and respond to them. Send outgoing requests and parse responses. Prepare and parse one-way messages.

As a convention, cXML refers to overall messages as "transmissions" to distinguish from Message type payloads. This library includes native Perl modules for the following transmission types:

Specifically NOT implemented are:

  • Attachments

  • Cryptographic signatures

  • Requesting the remote side's capabilities profile and restricting ourselves to it

Motivation & Future Development

While the above may implement a relatively small portion of the whole cXML specification, which is designed to describe every business-to-business transaction imaginable world-wide, it does fully satisfy our need (see "ACKNOWLEDGEMENTS") to act as a "punch-out" supplier to our Ariba/PeopleSoft/SAP/etc corporate clients.

The design is completely modular (see Business::cXML::Object) and uses XML::LibXML under the hood, to help simplify future efforts to cover more of the standard.

METHODS

new( %options )

Returns a fresh cXML handler.

Options useful to send requests and receive responses:

remote

HTTP/HTTPS URL where to POST requests

Options useful to process requests and emit responses:

local

HTTP/HTTPS URL to publish where clients can reach this handler

secret

Secret keyword expected by remote server

sender_callback

Subroutine, passed to "sender_callback()".

log_level

One of: CXML_LOG_NOTHING (default), CXML_LOG_ERR, CXML_LOG_WARN, CXML_LOG_INFO, CXML_LOG_DEBUG, CXML_LOG_TRACE. Alternates CXML_LOG_ERROR and CXML_LOG_WARNING are also available.

log_callback

Subroutine, passed to "log_callback()".

handlers

Hash of handlers, dereferenced and passed to "on()".

sender_callback( $sub )

By default, a request's From/Sender credentials are only used to guess response credentials. If you specify a callback here, it will be invoked immediately after XML parsing, before passing to transaction handlers, giving you an opportunity to authenticate the caller.

Your subroutine will be passed 3 arguments:

1. The current Business::cXML object
2. The Sender Business::cXML::Credential object
3. The From Business::cXML::Credential object

If you return a false value, the request will be deemed unauthorized and no handler will be invoked.

If you return anything else, it will be stored and available in the request sender's _note property. (See Business::cXML::Credential for details.)

Note that cXML From/Sender headers contain information about an entire company you're doing business with. The identity of the specific person triggering the request, if applicable, will be somewhere in contact or extrinsic data in the payload itself.

log_callback( $sub )

By default, basic details about log-worthy events are dumped to STDERR (filtered according to the current log level). By specifying your own handler, you can do anything else you'd like when informative events occur.

Your subroutine will be passed 5 arguments:

1. The current Business::cXML object
2. The level
CXML_LOG_ERR = 1 = fatal error (on our side)
CXML_LOG_WARN = 2 = warning (errors on the other end, network issues, etc.)
CXML_LOG_INFO = 3 = normal operations like receiving or sending transmissions
CXML_LOG_DEBUG = 4 = additional debugging information about processing requests
CXML_LOG_TRACE = 5 = full trace logging in some areas
3. A possible long-form message describing the event
4. A possible cXML transmission string (untouched if input)
5. A possible Business::cXML::Transmission object

Successful parsing of a new transmission triggers a level 3 log, whereas failure is a level 2. Failure to produce valid XML from our internal data (which should never occur) is a level 1.

NOTE: Logging is limited to this module. Thus, be sure to use process(), send() and stringify() to trap interesting events in the handling of Request, Response and Message transmissions.

on( %handlers )

Each key in %handlers is the bare name of a supported transaction type. For example, if you want to support PunchOutSetupRequest, the key is PunchOutSetup. Each key should point to a hashref specifying any options to declare in our Profile.

Special key __handler is mandatory and should point to a sub which will be called if its type of request is received, valid and optionally authenticated. Your handler will be passed 3 arguments:

1. The current Business::cXML object
2. The Business::cXML::Transmission request
3. A ready-to-fill Business::cXML::Transmission response

Be sure to change the response's status, which is a 500 error by default.

The response's to/from/sender is initialized in reciprocity to the request's, so your handler might not need to change much in those.

Keys represent the case-sensitive name of the cXML request without the redundant suffix. For example, a PunchOutSetupRequest is our type PunchOutSetup. Possible keys include: Order, PunchOutSetup, StatusUpdate, GetPending, Confirmation, ShipNotice, ProviderSetup, PaymentRemittance.

Pings and Profile requests are built-in, although you can override the handling of the latter with your own handler if you'd like.

process( [$input] )

Decodes the $input XML string which is expected to be a complete cXML transmission. May invoke one of your handlers declared with on() if appropriate.

Returns a string containing a valid cXML document at all times. This document includes any relevant status information determined during processing.

Note that an omitted or empty $input is actually valid and results in a "pong" response.

new_request( [$type] )

Returns a fresh Business::cXML::Transmission ready to be used as a request. Optional $type is a convenience shortcut to Business::cXML::Transmission::type(). The request's sender secret will be pre-filled.

send( $request )

Freeze $request, a Business::cXML::Transmission, and attempt sending it to the configured remote server. Returns the received response Business::cXML::Transmission on success, undef on failure. Note that as per Business::cXML::Transmission::new(), it is also possible that an error arrayref be returned instead of a transmission if parsing failed.

In case of failure, you may want to wait a certain amount of time and try again. To give you more options to that effect, $request can be either a Business::cXML::Transmission or a string.

new_message( [$type] )

Returns a fresh Business::cXML::Transmission ready to be used as a stand-alone message. Optional $type is passed on to Business::cXML::Transmission::type().

stringify( $message, %args )

Convenience wrapper around Business::cXML::Transmission::toForm() which allows you to trap logging events.

VERSION

0.6.12 based on cXML DTD 1.2.036

AUTHOR

Stéphane Lavergne https://github.com/vphantom

ACKNOWLEDGEMENTS

Graph X Design Inc. https://www.gxd.ca/ sponsored this project.

COPYRIGHT & LICENSE

Copyright (c) 2017-2018 Stéphane Lavergne https://github.com/vphantom

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.