From: David Croft Date: Sat, 15 Jul 2000 01:13:40 +0000 (+0000) Subject: @- New extension "pfpro" for interface with Signio Payflow Pro (David Croft) X-Git-Tag: PRE_FILE_COMPILE_API_CHANGE~267 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=467917cbdeb04e3bd2af5d22a9d098706eb80952;p=php @- New extension "pfpro" for interface with Signio Payflow Pro (David Croft) New extension pfpro to interface with Signio Payflow Pro library for credit card processing etc --- diff --git a/MAINTAINERS b/MAINTAINERS index 142b6fe12d..8c3995d2bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -87,6 +87,10 @@ EXTENSION: pdf PRIMARY MAINTAINER: Uwe Steinmann STATUS: Maintained -------------------------------------------------------------------------------- +EXTENSION: pfpro +PRIMARY MAINTAINER: David Croft +STATUS: Maintained +-------------------------------------------------------------------------------- EXTENSION: pgsql PRIMARY MAINTAINER: Jouni Ahto STATUS: Odd fixes diff --git a/ext/pfpro/Makefile.in b/ext/pfpro/Makefile.in new file mode 100644 index 0000000000..cbe71b99dc --- /dev/null +++ b/ext/pfpro/Makefile.in @@ -0,0 +1,8 @@ +# $Id$ + +LTLIBRARY_NAME = libpfpro.la +LTLIBRARY_SOURCES = pfpro.c +LTLIBRARY_SHARED_NAME = pfpro.la +LTLIBRARY_SHARED_LIBADD = $(PFPRO_SHARED_LIBADD) + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/pfpro/config.m4 b/ext/pfpro/config.m4 new file mode 100644 index 0000000000..9baaf9235f --- /dev/null +++ b/ext/pfpro/config.m4 @@ -0,0 +1,38 @@ +dnl $Id$ +dnl config.m4 for extension pfpro + +AC_MSG_CHECKING(for Payflow Pro support) + +PHP_ARG_WITH(pfpro, for pfpro support, +[ --with-pfpro=[DIR] Include Payflow Pro support]) + +if test "$PHP_PFPRO" != "no"; then + if test -r $PHP_PFPRO/lib/libpfpro.so; then + PFPRO_DIR=$PHP_PFPRO + else + AC_MSG_CHECKING(for libpfpro in default path) + for i in /usr/local /usr; do + if test -r $i/lib/libpfpro.so; then + PFPRO_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + if test -z "$PFPRO_DIR"; then + AC_MSG_RESULT(not found) + AC_MSG_ERROR(Please install the payflow pro SDK distribution - + pfpro.h should be in /include and + libpfpro.so should be in /lib) + Use ./configure --with-pfpro= if necessary + fi + + AC_ADD_INCLUDE($PFPRO_DIR/include) + + PHP_SUBST(PFPRO_SHARED_LIBADD) + + AC_ADD_LIBRARY_WITH_PATH(pfpro, $PFPRO_DIR, PFPRO_SHARED_LIBADD) + + AC_DEFINE(HAVE_PFPRO, 1, [ ]) + + PHP_EXTENSION(pfpro, $ext_shared) +fi diff --git a/ext/pfpro/pfpro.c b/ext/pfpro/pfpro.c new file mode 100644 index 0000000000..b0aba836cd --- /dev/null +++ b/ext/pfpro/pfpro.c @@ -0,0 +1,487 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: David Croft | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* Note: I've left the globals and ini-file handling stuff in + as I've yet to decide whether this is a good way to place "default" + stuff like hosts and proxies */ + +/* Status: Working. Awaiting comments from Signio as to whether + PNInit and ProcessPNTransaction have any meaningful return value */ + +#include "php.h" +#include "php_ini.h" +#include "php_pfpro.h" + +#include "pfpro.h" + +#if HAVE_PFPRO + +#ifdef ZTS +int pfpro_globals_id; +#else +php_pfpro_globals pfpro_globals; +#endif + +/* Function table */ + +function_entry pfpro_functions[] = { + PHP_FE(pfpro_version, NULL) + PHP_FE(pfpro_init, NULL) + PHP_FE(pfpro_cleanup, NULL) + PHP_FE(pfpro_process_raw, NULL) + PHP_FE(pfpro_process, NULL) + {NULL, NULL, NULL} +}; + +zend_module_entry pfpro_module_entry = { + "pfpro", + pfpro_functions, + PHP_MINIT(pfpro), + PHP_MSHUTDOWN(pfpro), + NULL, /* request start */ + NULL, /* request end */ + PHP_MINFO(pfpro), + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_PFPRO +ZEND_GET_MODULE(pfpro) +#endif + +/* +PHP_INI_BEGIN() +PHP_INI_END() +*/ + +PHP_MINIT_FUNCTION(pfpro) +{ +/* + REGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(pfpro) +{ +/* + UNREGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(pfpro) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Signio Payflow Pro support", "enabled"); + php_info_print_table_end(); + + /* + DISPLAY_INI_ENTRIES(); + */ +} + + + +/* {{{ proto string pfpro_version() + Returns the version of the Payflow Pro library */ +PHP_FUNCTION(pfpro_version) +{ + if (ZEND_NUM_ARGS() != 0) { + WRONG_PARAM_COUNT; + } + + RETURN_STRING(PNVersion(), 1); +} +/* }}} */ + + + +/* {{{ proto void pfpro_init() + Initialises the Payflow Pro library */ +PHP_FUNCTION(pfpro_init) +{ + if (ZEND_NUM_ARGS() != 0) { + WRONG_PARAM_COUNT; + } + + PNInit(); + + RETURN_TRUE; +} +/* }}} */ + + + +/* {{{ proto void pfpro_cleanup() + Shuts down the Payflow Pro library */ +PHP_FUNCTION(pfpro_cleanup) +{ + if (ZEND_NUM_ARGS() != 0) { + WRONG_PARAM_COUNT; + } + + PNCleanup(); + + RETURN_TRUE; +} +/* }}} */ + + + +/* {{{ proto string pfpro_process_raw(string parmlist [, string hostaddress [, int port, [, int timeout [, string proxyAddress [, int proxyPort [, string proxyLogon [, string proxyPassword]]]]]]]) + Raw Payflow Pro transaction processing */ +PHP_FUNCTION(pfpro_process_raw) +{ + pval ***args; + + char *parmlist; + char *address = NULL; + int port = 443; + int timeout = 30; + char *proxyAddress = NULL; + int proxyPort = 0; + char *proxyLogon = NULL; + char *proxyPassword = NULL; + + int freeaddress = 0; + + /* No, I don't like that Signio tell you to use a + fixed length buffer either */ + + char response[512]; + + if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 8) { + WRONG_PARAM_COUNT; + } + + args = (pval ***) emalloc(sizeof(pval **) * ZEND_NUM_ARGS()); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) { + php_error(E_WARNING, "Unable to read parameters in pfpro_process_raw()"); + efree(args); + RETURN_FALSE; + } + + switch (ZEND_NUM_ARGS()) { + case 8: + convert_to_string_ex(args[7]); + proxyPassword = (*args[7])->value.str.val; + /* fall through */ + + case 7: + convert_to_string_ex(args[6]); + proxyLogon = (*args[6])->value.str.val; + /* fall through */ + + case 6: + convert_to_long_ex(args[5]); + proxyPort = (*args[5])->value.lval; + /* fall through */ + + case 5: + convert_to_string_ex(args[4]); + proxyAddress = (*args[4])->value.str.val; + /* fall through */ + + case 4: + convert_to_long_ex(args[3]); + timeout = (*args[3])->value.lval; + /* fall through */ + + case 3: + convert_to_long_ex(args[2]); + port = (*args[2])->value.lval; + /* fall through */ + + case 2: + convert_to_string_ex(args[1]); + address = (*args[1])->value.str.val; + } + + convert_to_string_ex(args[0]); + parmlist = (*args[0])->value.str.val; + + efree(args); + + /* Default to signio's test server */ + + if (address == NULL) { + address = estrdup("test.signio.com"); + freeaddress = 1; + } + +#if 0 + printf("Address: >%s<\n", address); + printf("Port: >%d<\n", port); + printf("Parmlist: >%s<\n", parmlist); + printf("Timeout: >%d<\n", timeout); + printf("Proxy address: >%s<\n", proxyAddress); + printf("Proxy port: >%d<\n", proxyPort); + printf("Proxy logon: >%s<\n", proxyLogon); + printf("Proxy password: >%s<\n", proxyPassword); +#endif + + /* Blank the response buffer */ + + memset(response, 0, sizeof(response)); + + /* Perform the transaction */ + + ProcessPNTransaction(address, port, proxyAddress, proxyPort, proxyLogon, proxyPassword, parmlist, strlen(parmlist), timeout, response); + + if (freeaddress) { + efree(address); + } + + RETURN_STRING(response, 1); +} +/* }}} */ + + + +/* {{{ proto array pfpro_process(array parmlist [, string hostaddress [, int port, [, int timeout [, string proxyAddress [, int proxyPort [, string proxyLogon [, string proxyPassword]]]]]]]) + Payflow Pro transaction processing using arrays */ +PHP_FUNCTION(pfpro_process) +{ + pval ***args; + + HashTable *target_hash; + ulong lkey; + char *varname; + zval **entry; + int pass; + + char *address = NULL; + int port = 443; + int timeout = 30; + char *proxyAddress = NULL; + int proxyPort = 0; + char *proxyLogon = NULL; + char *proxyPassword = NULL; + int freeaddress = 0; + + char *parmlist = NULL; + int parmlength = 0; + + /* No, I don't like that Signio tell you to use a + fixed length buffer either */ + + char response[512]; + + char tmpbuf[128]; + char *rsppos, *valpos; + + + if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 8) { + WRONG_PARAM_COUNT; + } + + args = (pval ***) emalloc(sizeof(pval **) * ZEND_NUM_ARGS()); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) { + php_error(E_WARNING, "Unable to read parameters in pfpro_process()"); + efree(args); + RETURN_FALSE; + } + + if ((*args[0])->type != IS_ARRAY) { + php_error(E_WARNING, "First parameter to pfpro_process() must be an array"); + efree(args); + RETURN_FALSE; + } + + switch (ZEND_NUM_ARGS()) { + case 8: + convert_to_string_ex(args[7]); + proxyPassword = (*args[7])->value.str.val; + /* fall through */ + + case 7: + convert_to_string_ex(args[6]); + proxyLogon = (*args[6])->value.str.val; + /* fall through */ + + case 6: + convert_to_long_ex(args[5]); + proxyPort = (*args[5])->value.lval; + /* fall through */ + + case 5: + convert_to_string_ex(args[4]); + proxyAddress = (*args[4])->value.str.val; + /* fall through */ + + case 4: + convert_to_long_ex(args[3]); + timeout = (*args[3])->value.lval; + /* fall through */ + + case 3: + convert_to_long_ex(args[2]); + port = (*args[2])->value.lval; + /* fall through */ + + case 2: + convert_to_string_ex(args[1]); + address = (*args[1])->value.str.val; + } + + /* Concatenate the passed array as specified by signio. + Basically it's all key=value&key=value, the only exception + being if the value contains = or &, in which case we also + encode the length, e.g. key[5]=bl&ah */ + + target_hash = HASH_OF(*args[0]); + + for (pass = 0; pass <= 1; pass ++) { + + parmlength = 0; + /* we go around the array twice. the first time to calculate + the string length, the second time to actually store it */ + + zend_hash_internal_pointer_reset(target_hash); + + while (zend_hash_get_current_data(target_hash, (void **)&entry) == SUCCESS) { + if (zend_hash_get_current_key(target_hash, &varname, &lkey) == HASH_KEY_IS_STRING) { + if (parmlength > 0) { + if (pass == 1) + strcpy(parmlist + parmlength, "&"); + parmlength += 1; + } + + if (pass == 1) + strcpy(parmlist + parmlength, varname); + parmlength += strlen(varname); + + if ((*entry)->type == IS_STRING) { + if (strchr((*entry)->value.str.val, '&') || + strchr((*entry)->value.str.val, '=')) { + sprintf(tmpbuf, "[%d]=", (*entry)->value.str.len); + if (pass == 1) + strcpy(parmlist + parmlength, tmpbuf); + parmlength += strlen(tmpbuf); + } + else { + if (pass == 1) + strcpy(parmlist + parmlength, "="); + parmlength += 1; + } + if (pass == 1) + strcpy(parmlist + parmlength, (*entry)->value.str.val); + parmlength += (*entry)->value.str.len; + } + else if ((*entry)->type == IS_LONG) { + sprintf(tmpbuf, "=%d", (*entry)->value.lval); + if (pass == 1) + strcpy(parmlist + parmlength, tmpbuf); + parmlength += strlen(tmpbuf); + } + else if ((*entry)->type == IS_DOUBLE) { + sprintf(tmpbuf, "=%.2f", (*entry)->value.dval); + if (pass == 1) + strcpy(parmlist + parmlength, tmpbuf); + parmlength += strlen(tmpbuf); + } + else { + php_error(E_WARNING, "pfpro_process() array values should be strings, ints or floats"); + } + efree(varname); + } + else { + php_error(E_WARNING, "pfpro_process() array keys must be strings"); + efree(args); + RETURN_FALSE; + } + zend_hash_move_forward(target_hash); + } + + if (pass == 0) { + parmlist = emalloc(parmlength + 1); + } + } + + efree(args); + + /* Default to signio's test server */ + + if (address == NULL) { + /* is it safe to just do address = "test.signio.com"; here? */ + address = estrdup("test.signio.com"); + freeaddress = 1; + } + +#if 0 + printf("Address: >%s<\n", address); + printf("Port: >%d<\n", port); + printf("Parmlist: >%s<\n", parmlist); + printf("Timeout: >%d<\n", timeout); + printf("Proxy address: >%s<\n", proxyAddress); + printf("Proxy port: >%d<\n", proxyPort); + printf("Proxy logon: >%s<\n", proxyLogon); + printf("Proxy password: >%s<\n", proxyPassword); +#endif + + /* Allocate the array for the response now - so we catch any errors + from this BEFORE we knock it off to the bank */ + + if (array_init(return_value) == FAILURE) { + php_error(E_WARNING, "pfpro_process() unable to create array"); + RETURN_FALSE; + } + + /* Blank the response buffer */ + + memset(response, 0, sizeof(response)); + + /* Perform the transaction */ + + ProcessPNTransaction(address, port, proxyAddress, proxyPort, proxyLogon, proxyPassword, parmlist, parmlength, timeout, response); + + if (freeaddress) { + efree(address); + } + + /* Decode the response back into a PHP array */ + + rsppos = strtok(response, "&"); + + do { + valpos = strchr(rsppos, '='); + if (valpos) { + strncpy(tmpbuf, rsppos, valpos - rsppos); + tmpbuf[valpos - rsppos] = 0; + add_assoc_string(return_value, tmpbuf, valpos + 1, 1); + } + + } while (rsppos = strtok(NULL, "&")); +} +/* }}} */ + + + +#endif /* HAVE_PFPRO */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/pfpro/php_pfpro.h b/ext/pfpro/php_pfpro.h new file mode 100644 index 0000000000..5992c2d151 --- /dev/null +++ b/ext/pfpro/php_pfpro.h @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: David Croft | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef _PHP_PFPRO_H +#define _PHP_PFPRO_H + +#if HAVE_PFPRO + +extern zend_module_entry pfpro_module_entry; +#define phpext_pfpro_ptr &pfpro_module_entry + +#ifdef PHP_WIN32 +#define PHP_PFPRO_API __declspec(dllexport) +#else +#define PHP_PFPRO_API +#endif + +PHP_MINIT_FUNCTION(pfpro); +PHP_MSHUTDOWN_FUNCTION(pfpro); +PHP_RINIT_FUNCTION(pfpro); +PHP_RSHUTDOWN_FUNCTION(pfpro); +PHP_MINFO_FUNCTION(pfpro); + +PHP_FUNCTION(pfpro_version); /* Return library version */ +PHP_FUNCTION(pfpro_init); +PHP_FUNCTION(pfpro_cleanup); +PHP_FUNCTION(pfpro_process_raw); +PHP_FUNCTION(pfpro_process); + +/* Fill in this structure and use entries in it + for thread safety instead of using true globals. +*/ +typedef struct { + /* You can use the next one as type if your module registers any + resources. Oh, you can of course rename it to something more + suitable, add list entry types or remove it if it not needed. + It's just an example. + */ + int le_pfpro; +} php_pfpro_globals; + +/* In every function that needs to use variables in php_pfpro_globals, + do call PFPROLS_FETCH(); after declaring other variables used by + that function, and always refer to them as PFPROG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define PFPROG(v) (pfpro_globals->v) +#define PFPROLS_FETCH() php_pfpro_globals *pfpro_globals = ts_resource(gd_pfpro_id) +#else +#define PFPROG(v) (pfpro_globals.v) +#define PFPROLS_FETCH() +#endif + +#else + +#define phpext_pfpro_ptr NULL + +#endif + +#endif /* _PHP_PFPRO_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/pfpro/test.output b/ext/pfpro/test.output new file mode 100644 index 0000000000..29e13f7a62 --- /dev/null +++ b/ext/pfpro/test.output @@ -0,0 +1,29 @@ +X-Powered-By: PHP/4.0.2-dev +Content-type: text/html + +
+
+Payflow Pro library is version L211
+Payflow Pro init returned 1
+Signio response code was 0, which means: Approved
+
+Dump of the transaction request Array
+(
+    [USER] => mylogin
+    [PWD] => mypassword
+    [TRXTYPE] => S
+    [TENDER] => C
+    [AMT] => 1.5
+    [ACCT] => 4111111111111111
+    [EXPDATE] => 0900
+)
+
+Dump of the response Array
+(
+    [RESULT] => 0
+    [PNREF] => P40111660030
+    [RESPMSG] => Approved
+    [AUTHCODE] => 251PNI
+    [AVSADDR] => X
+    [AVSZIP] => X
+)
diff --git a/ext/pfpro/test.php b/ext/pfpro/test.php
new file mode 100644
index 0000000000..712cc073cb
--- /dev/null
+++ b/ext/pfpro/test.php
@@ -0,0 +1,43 @@
+\n\n";
+
+echo "Payflow Pro library is version ".pfpro_version()."\n";
+
+echo "Payflow Pro init returned ".pfpro_init()."\n";
+
+$transaction = array(USER	=> 'mylogin',
+		     PWD	=> 'mypassword',
+		     TRXTYPE	=> 'S',
+		     TENDER	=> 'C',
+		     AMT	=> 1.50,
+		     ACCT	=> '4111111111111111',
+		     EXPDATE	=> '0900'
+		     );
+
+$response = pfpro_process($transaction);
+
+if (!$response) {
+  die("Couldn't establish link to signio software.\n");
+}
+
+echo "Signio response code was ".$response[RESULT];
+echo ", which means: ".$response[RESPMSG]."\n";
+
+echo "\n";
+
+echo "Dump of the transaction request ";
+print_r($transaction);
+
+echo "\n";
+
+echo "Dump of the response ";
+print_r($response);
+
+pfpro_cleanup()
+
+?>