Index: trunk/extensions/DonationInterface/donationinterface.php |
— | — | @@ -0,0 +1,518 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Donation Interface |
| 6 | + * |
| 7 | + * To install the DontaionInterface extension, put the following line in LocalSettings.php: |
| 8 | + * require_once( "\$IP/extensions/DonationInterface/donationinterface.php" ); |
| 9 | + * |
| 10 | + */ |
| 11 | + |
| 12 | + |
| 13 | +# Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly. |
| 14 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 15 | + echo <<<EOT |
| 16 | +To install the DontaionInterface extension, put the following line in LocalSettings.php: |
| 17 | +require_once( "\$IP/extensions/DonationInterface/donationinterface.php" ); |
| 18 | +EOT; |
| 19 | + exit( 1 ); |
| 20 | +} |
| 21 | + |
| 22 | +// Extension credits that will show up on Special:Version |
| 23 | +$wgExtensionCredits['specialpage'][] = array( |
| 24 | + 'name' => 'Donation Interface', |
| 25 | + 'author' => 'Katie Horn', |
| 26 | + 'version' => '1.0.0', |
| 27 | + 'descriptionmsg' => 'donationinterface-desc', |
| 28 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:DonationInterface', |
| 29 | +); |
| 30 | + |
| 31 | +$donationinterface_dir = dirname( __FILE__ ) . '/'; |
| 32 | + |
| 33 | +/** |
| 34 | + * Figure out what we've got enabled. |
| 35 | + */ |
| 36 | + |
| 37 | +$optionalParts = array( //define as fail closed. This variable will be unset before we leave this file. |
| 38 | + 'Extras' => false, //this one gets set in the next loop, so don't bother. |
| 39 | + 'Stomp' => false, |
| 40 | + 'CustomFilters' => false, //this is definitely an Extra |
| 41 | + 'ConversionLog' => false, //this is definitely an Extra |
| 42 | + 'Minfraud' => false, //this is definitely an Extra |
| 43 | + 'Minfraud_as_filter' => false, //extra |
| 44 | + 'Recaptcha' => false, //extra |
| 45 | + 'PayflowPro' => false, |
| 46 | + 'GlobalCollect' => false, |
| 47 | + |
| 48 | +); |
| 49 | + |
| 50 | +foreach ($optionalParts as $subextension => $enabled){ |
| 51 | + $globalname = 'wgDonationInterfaceEnable' . $subextension; |
| 52 | + global $$globalname; |
| 53 | + if ( isset( $$globalname ) && $$globalname === true ) { |
| 54 | + $optionalParts[$subextension] = true; |
| 55 | + if ( $subextension === 'CustomFilters' || |
| 56 | + $subextension === 'ConversionLog' || |
| 57 | + $subextension === 'Minfraud' || |
| 58 | + $subextension === 'Recaptcha' ) { |
| 59 | + |
| 60 | + $optionalParts['Extras'] = true; |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | + |
| 66 | +/** |
| 67 | + * CLASSES |
| 68 | + */ |
| 69 | +$wgAutoloadClasses['DonationData'] = $donationinterface_dir . 'gateway_common/DonationData.php'; |
| 70 | +$wgAutoloadClasses['GatewayAdapter'] = $donationinterface_dir . 'gateway_common/gateway.adapter.php'; |
| 71 | +$wgAutoloadClasses['GatewayForm'] = $donationinterface_dir . 'gateway_common/GatewayForm.php'; |
| 72 | + |
| 73 | +//load all possible form classes |
| 74 | +$wgAutoloadClasses['Gateway_Form'] = $donationinterface_dir . 'gateway_forms/Form.php'; |
| 75 | +$wgAutoloadClasses['Gateway_Form_OneStepTwoColumn'] = $donationinterface_dir . 'gateway_forms/OneStepTwoColumn.php'; |
| 76 | +$wgAutoloadClasses['Gateway_Form_TwoStepAmount'] = $donationinterface_dir . 'gateway_forms/TwoStepAmount.php'; |
| 77 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumn'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumn.php'; |
| 78 | +$wgAutoloadClasses['Gateway_Form_TwoColumnPayPal'] = $donationinterface_dir . 'gateway_forms/TwoColumnPayPal.php'; |
| 79 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter.php'; |
| 80 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter2'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter2.php'; |
| 81 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter3'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter3.php'; |
| 82 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter4'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter4.php'; |
| 83 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter5'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter5.php'; |
| 84 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter6'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter6.php'; |
| 85 | +$wgAutoloadClasses['Gateway_Form_TwoColumnLetter7'] = $donationinterface_dir . 'gateway_forms/TwoColumnLetter7.php'; |
| 86 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter.php'; |
| 87 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetterCA'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetterCA.php'; |
| 88 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter2'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter2.php'; |
| 89 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnLetter3'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnLetter3.php'; |
| 90 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnPremium'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremium.php'; |
| 91 | +$wgAutoloadClasses['Gateway_Form_TwoStepTwoColumnPremiumUS'] = $donationinterface_dir . 'gateway_forms/TwoStepTwoColumnPremiumUS.php'; |
| 92 | +$wgAutoloadClasses['Gateway_Form_RapidHtml'] = $donationinterface_dir . 'gateway_forms/RapidHtml.php'; |
| 93 | +$wgAutoloadClasses['Gateway_Form_SingleColumn'] = $donationinterface_dir . 'gateway_forms/SingleColumn.php'; |
| 94 | + |
| 95 | + |
| 96 | +//GlobalCollect gateway classes |
| 97 | +if ( $optionalParts['GlobalCollect'] === true ){ |
| 98 | + $wgAutoloadClasses['GlobalCollectGateway'] = $donationinterface_dir . 'globalcollect_gateway/globalcollect_gateway.body.php'; |
| 99 | + $wgAutoloadClasses['GlobalCollectGatewayResult'] = $donationinterface_dir . 'globalcollect_gateway/globalcollect_resultswitcher.body.php'; |
| 100 | + $wgAutoloadClasses['GlobalCollectAdapter'] = $donationinterface_dir . 'globalcollect_gateway/globalcollect.adapter.php'; |
| 101 | +} |
| 102 | +//PayflowPro gateway classes |
| 103 | +if ( $optionalParts['PayflowPro'] === true ){ |
| 104 | + $wgAutoloadClasses['PayflowProGateway'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.body.php'; |
| 105 | + $wgAutoloadClasses['PayflowProAdapter'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro.adapter.php'; |
| 106 | +} |
| 107 | + |
| 108 | +//Stomp classes |
| 109 | +if ($optionalParts['Stomp'] === true){ |
| 110 | + $wgAutoloadClasses['activemq_stomp'] = $donationinterface_dir . 'activemq_stomp/activemq_stomp.php'; # Tell MediaWiki to load the extension body. |
| 111 | +} |
| 112 | + |
| 113 | +//Extras classes - required for ANY optional class that is considered an "extra". |
| 114 | +if ($optionalParts['Extras'] === true){ |
| 115 | + $wgAutoloadClasses['Gateway_Extras'] = $donationinterface_dir . "extras/extras.body.php"; |
| 116 | +} |
| 117 | + |
| 118 | +//Custom Filters classes |
| 119 | +if ($optionalParts['CustomFilters'] === true){ |
| 120 | + $wgAutoloadClasses['Gateway_Extras_CustomFilters'] = $donationinterface_dir . "extras/custom_filters/custom_filters.body.php"; |
| 121 | +} |
| 122 | + |
| 123 | +//Conversion Log classes |
| 124 | +if ($optionalParts['ConversionLog'] === true){ |
| 125 | + $wgAutoloadClasses['Gateway_Extras_ConversionLog'] = $donationinterface_dir . "extras/conversion_log/conversion_log.body.php"; |
| 126 | +} |
| 127 | + |
| 128 | +//Minfraud classes |
| 129 | +if ( $optionalParts['Minfraud'] === true || $optionalParts['Minfraud_as_filter'] === true ){ |
| 130 | + $wgAutoloadClasses['Gateway_Extras_MinFraud'] = $donationinterface_dir . "extras/minfraud/minfraud.body.php"; |
| 131 | +} |
| 132 | + |
| 133 | +//Minfraud as Filter classes |
| 134 | +if ( $optionalParts['Minfraud_as_filter'] === true ){ |
| 135 | + $wgAutoloadClasses['Gateway_Extras_CustomFilters_MinFraud'] = $donationinterface_dir . "extras/custom_filters/filters/minfraud/minfraud.body.php"; |
| 136 | +} |
| 137 | + |
| 138 | +//Recaptcha classes |
| 139 | +if ( $optionalParts['Recaptcha'] === true ){ |
| 140 | + $wgAutoloadClasses['Gateway_Extras_ReCaptcha'] = $donationinterface_dir . "extras/recaptcha/recaptcha.body.php"; |
| 141 | +} |
| 142 | + |
| 143 | + |
| 144 | +/** |
| 145 | + * GLOBALS |
| 146 | + */ |
| 147 | + |
| 148 | +/** |
| 149 | + * Global form dir and RapidHTML whitelist |
| 150 | + */ |
| 151 | +$wgDonationInterfaceHtmlFormDir = dirname( __FILE__ ) . "/gateway_forms/html"; |
| 152 | +//ffname is the $key from now on. |
| 153 | +$wgDonationInterfaceAllowedHtmlForms = array( |
| 154 | + 'demo' => $wgDonationInterfaceHtmlFormDir . "/demo.html", |
| 155 | + 'globalcollect_test' => $wgDonationInterfaceHtmlFormDir . "/globalcollect_test.html", |
| 156 | +); |
| 157 | + |
| 158 | +$wgDonationInterfaceTest = false; |
| 159 | + |
| 160 | +/** |
| 161 | + * The URL to redirect a transaction to PayPal |
| 162 | + * This should probably point to ContributionTracking. |
| 163 | + */ |
| 164 | +$wgDonationInterfacePaypalURL = ''; |
| 165 | +$wgDonationInterfaceRetrySeconds = 5; |
| 166 | + |
| 167 | +//all of the following variables make sense to override directly, |
| 168 | +//or change "DonationInterface" to the gateway's id to override just for that gateway. |
| 169 | +//for instance: To override $wgDonationInterfaceUseSyslog just for GlobalCollect, add |
| 170 | +// $wgGolbalCollectGatewayUseSyslog = true |
| 171 | +// to LocalSettings. |
| 172 | +// |
| 173 | + |
| 174 | +$wgDonationInterfaceDisplayDebug = false; |
| 175 | +$wgDonationInterfaceUseSyslog = false; |
| 176 | +$wgDonationInterfaceSaveCommStats = false; |
| 177 | + |
| 178 | +$wgDonationInterfaceCSSVersion = 1; |
| 179 | +$wgDonationInterfaceTimeout = 5; |
| 180 | +$wgDonationInterfaceDefaultForm = 'TwoStepTwoColumn'; |
| 181 | + |
| 182 | +/** |
| 183 | + * A string or array of strings for making tokens more secure |
| 184 | + * |
| 185 | + * Please set this! If you do not, tokens are easy to get around, which can |
| 186 | + * potentially leave you and your users vulnerable to CSRF or other forms of |
| 187 | + * attack. |
| 188 | + */ |
| 189 | +$wgDonationInterfaceSalt = $wgSecretKey; |
| 190 | + |
| 191 | +/** |
| 192 | + * A string that can contain wikitext to display at the head of the credit card form |
| 193 | + * |
| 194 | + * This string gets run like so: $wg->addHtml( $wg->Parse( $wgpayflowGatewayHeader )) |
| 195 | + * You can use '@language' as a placeholder token to extract the user's language. |
| 196 | + * |
| 197 | + */ |
| 198 | +$wgDonationInterfaceHeader = NULL; |
| 199 | + |
| 200 | +/** |
| 201 | + * A string containing full URL for Javascript-disabled credit card form redirect |
| 202 | + */ |
| 203 | +$wgDonationInterfaceNoScriptRedirect = null; |
| 204 | + |
| 205 | +/** |
| 206 | + * Proxy settings |
| 207 | + * |
| 208 | + * If you need to use an HTTP proxy for outgoing traffic, |
| 209 | + * set wgPayflowGatweayUseHTTPProxy=TRUE and set $wgPayflowProGatewayHTTPProxy |
| 210 | + * to the proxy desination. |
| 211 | + * eg: |
| 212 | + * $wgPayflowProGatewayUseHTTPProxy=TRUE; |
| 213 | + * $wgPayflowProGatewayHTTPProxy='192.168.1.1:3128' |
| 214 | + */ |
| 215 | +$wgDonationInterfaceUseHTTPProxy = FALSE; |
| 216 | +$wgDonationInterfaceHTTPProxy = ''; |
| 217 | + |
| 218 | +/** |
| 219 | + * Set the max-age value for Squid |
| 220 | + * |
| 221 | + * If you have Squid enabled for caching, use this variable to configure |
| 222 | + * the s-max-age for cached requests. |
| 223 | + * @var int Time in seconds |
| 224 | + */ |
| 225 | +$wgDonationInterfaceSMaxAge = 6000; |
| 226 | + |
| 227 | +/** |
| 228 | + * Configure price cieling and floor for valid contribution amount. Values |
| 229 | + * should be in USD. |
| 230 | + */ |
| 231 | +$wgDonationInterfacePriceFloor = '1.00'; |
| 232 | +$wgDonationInterfacePriceCeiling = '10000.00'; |
| 233 | + |
| 234 | +/** |
| 235 | + * Default Thank You and Fail pages for all of donationinterface - language will be calc'd and appended at runtime. |
| 236 | + */ |
| 237 | +//$wgDonationInterfaceThankYouPage = 'https://wikimediafoundation.org/wiki/Thank_You'; |
| 238 | +$wgDonationInterfaceThankYouPage = 'Donate-thanks'; |
| 239 | +$wgDonationInterfaceFailPage = 'Donate-error'; |
| 240 | + |
| 241 | + |
| 242 | +//GlobalCollect gateway globals |
| 243 | +if ( $optionalParts['GlobalCollect'] === true ){ |
| 244 | + $wgGlobalCollectGatewayURL = 'https://ps.gcsip.nl/wdl/wdl'; |
| 245 | + $wgGlobalCollectGatewayTestingURL = 'https://'; // GlobalCollect testing URL |
| 246 | + |
| 247 | + $wgGlobalCollectGatewayMerchantID = ''; // GlobalCollect ID |
| 248 | + |
| 249 | + $wgGlobalCollectGatewayHtmlFormDir = $donationinterface_dir . 'globalcollect_gateway/forms/html'; |
| 250 | + //this really should be redefined in LocalSettings. |
| 251 | + $wgGlobalCollectGatewayAllowedHtmlForms = $wgDonationInterfaceAllowedHtmlForms; |
| 252 | +} |
| 253 | + |
| 254 | +//PayflowPro gateway globals |
| 255 | +if ( $optionalParts['PayflowPro'] === true ){ |
| 256 | + $wgPayflowProGatewayURL = 'https://payflowpro.paypal.com'; |
| 257 | + $wgPayflowProGatewayTestingURL = 'https://pilot-payflowpro.paypal.com'; // Payflow testing URL |
| 258 | + |
| 259 | + $wgPayflowProGatewayPartnerID = ''; // PayPal or original authorized reseller |
| 260 | + $wgPayflowProGatewayVendorID = ''; // paypal merchant login ID |
| 261 | + $wgPayflowProGatewayUserID = ''; // if one or more users are set up, authorized user ID, else same as VENDOR |
| 262 | + $wgPayflowProGatewayPassword = ''; // merchant login password |
| 263 | + |
| 264 | + $wgPayflowProGatewayHtmlFormDir = $donationinterface_dir . 'payflowpro_gateway/forms/html'; |
| 265 | + //this really should be redefined in LocalSettings. |
| 266 | + $wgPayflowProGatewayAllowedHtmlForms = $wgDonationInterfaceAllowedHtmlForms; |
| 267 | +} |
| 268 | + |
| 269 | +//Stomp globals |
| 270 | +if ($optionalParts['Stomp'] === true){ |
| 271 | + $wgStompServer = ""; |
| 272 | + //$wgStompQueueName = ""; //only set this with an actual value. Default is unset. |
| 273 | + //$wgPendingStompQueueName = ""; //only set this with an actual value. Default is unset. |
| 274 | +} |
| 275 | + |
| 276 | +//Extras globals - required for ANY optional class that is considered an "extra". |
| 277 | +if ($optionalParts['Extras'] === true){ |
| 278 | + $wgDonationInterfaceExtrasLog = ''; |
| 279 | +} |
| 280 | + |
| 281 | +//Custom Filters globals |
| 282 | +if ( $optionalParts['CustomFilters'] === true ){ |
| 283 | + //Define the action to take for a given $risk_score |
| 284 | + $wgDonationInterfaceCustomFiltersActionRanges = array( |
| 285 | + 'process' => array( 0, 100 ), |
| 286 | + 'review' => array( -1, -1 ), |
| 287 | + 'challenge' => array( -1, -1 ), |
| 288 | + 'reject' => array( -1, -1 ), |
| 289 | + ); |
| 290 | + |
| 291 | + /** |
| 292 | + * A value for tracking the 'riskiness' of a transaction |
| 293 | + * |
| 294 | + * The action to take based on a transaction's riskScore is determined by |
| 295 | + * $action_ranges. This is built assuming a range of possible risk scores |
| 296 | + * as 0-100, although you can probably bend this as needed. |
| 297 | + */ |
| 298 | + $wgDonationInterfaceCustomFiltersRiskScore = 0; |
| 299 | +} |
| 300 | + |
| 301 | +//Minfraud globals |
| 302 | +if ( $optionalParts['Minfraud'] === true || $optionalParts['Minfraud_as_filter'] === true ){ |
| 303 | + /** |
| 304 | + * Your minFraud license key. |
| 305 | + */ |
| 306 | + $wgMinFraudLicenseKey = ''; |
| 307 | + |
| 308 | + /** |
| 309 | + * Set the risk score ranges that will cause a particular 'action' |
| 310 | + * |
| 311 | + * The keys to the array are the 'actions' to be taken (eg 'process'). |
| 312 | + * The value for one of these keys is an array representing the lower |
| 313 | + * and upper bounds for that action. For instance, |
| 314 | + * $wgMinFraudActionRagnes = array( |
| 315 | + * 'process' => array( 0, 100) |
| 316 | + * ... |
| 317 | + * ); |
| 318 | + * means that any transaction with a risk score greather than or equal |
| 319 | + * to 0 and less than or equal to 100 will be given the 'process' action. |
| 320 | + * |
| 321 | + * These are evauluated on a >= or <= basis. Please refer to minFraud |
| 322 | + * documentation for a thorough explanation of the 'riskScore'. |
| 323 | + */ |
| 324 | + $wgMinFraudActionRanges = array( |
| 325 | + 'process' => array( 0, 100 ), |
| 326 | + 'review' => array( -1, -1 ), |
| 327 | + 'challenge' => array( -1, -1 ), |
| 328 | + 'reject' => array( -1, -1 ) |
| 329 | + ); |
| 330 | + |
| 331 | + // Timeout in seconds for communicating with MaxMind |
| 332 | + $wgMinFraudTimeout = 2; |
| 333 | + |
| 334 | + /** |
| 335 | + * Define whether or not to run minFraud in stand alone mode |
| 336 | + * |
| 337 | + * If this is set to run in standalone, these scripts will be |
| 338 | + * accessed directly via the "GatewayValidate" hook. |
| 339 | + * You may not want to run this in standalone mode if you prefer |
| 340 | + * to use this in conjunction with Custom Filters. This has the |
| 341 | + * advantage of sharing minFraud info with other filters. |
| 342 | + */ |
| 343 | + $wgMinFraudStandalone = TRUE; |
| 344 | + |
| 345 | +} |
| 346 | + |
| 347 | +//Minfraud as Filter globals |
| 348 | +if ( $optionalParts['Minfraud_as_filter'] === true ){ |
| 349 | + $wgMinFraudStandalone = FALSE; |
| 350 | +} |
| 351 | + |
| 352 | +//Recaptcha globals |
| 353 | +if ( $optionalParts['Recaptcha'] === true ){ |
| 354 | + /** |
| 355 | + * Public and Private reCaptcha keys |
| 356 | + * |
| 357 | + * These can be obtained at: |
| 358 | + * http://www.google.com/recaptcha/whyrecaptcha |
| 359 | + */ |
| 360 | + $wgDonationInterfaceRecaptchaPublicKey = ''; |
| 361 | + $wgDonationInterfaceRecaptchaPrivateKey = ''; |
| 362 | + |
| 363 | + // Timeout (in seconds) for communicating with reCatpcha |
| 364 | + $wgDonationInterfaceRecaptchaTimeout = 2; |
| 365 | + |
| 366 | + /** |
| 367 | + * HTTP Proxy settings |
| 368 | + */ |
| 369 | + $wgDonationInterfaceRecaptchaUseHTTPProxy = false; |
| 370 | + $wgDonationInterfaceRecaptchaHTTPProxy = false; |
| 371 | + |
| 372 | + /** |
| 373 | + * Use SSL to communicate with reCaptcha |
| 374 | + */ |
| 375 | + $wgDonationInterfaceRecaptchaUseSSL = 1; |
| 376 | + |
| 377 | + /** |
| 378 | + * The # of times to retry communicating with reCaptcha if communication fails |
| 379 | + * @var int |
| 380 | + */ |
| 381 | + $wgDonationInterfaceRecaptchaComsRetryLimit = 3; |
| 382 | +} |
| 383 | + |
| 384 | +/** |
| 385 | + * SPECIAL PAGES |
| 386 | + */ |
| 387 | + |
| 388 | +//GlobalCollect gateway special pages |
| 389 | +if ( $optionalParts['GlobalCollect'] === true ){ |
| 390 | + $wgSpecialPages['GlobalCollectGateway'] = 'GlobalCollectGateway'; |
| 391 | + $wgSpecialPages['GlobalCollectGatewayResult'] = 'GlobalCollectGatewayResult'; |
| 392 | +} |
| 393 | +//PayflowPro gateway special pages |
| 394 | +if ( $optionalParts['PayflowPro'] === true ){ |
| 395 | + $wgSpecialPages['PayflowProGateway'] = 'PayflowProGateway'; |
| 396 | +} |
| 397 | + |
| 398 | + |
| 399 | +/** |
| 400 | + * HOOKS |
| 401 | + */ |
| 402 | + |
| 403 | +//Unit tests |
| 404 | +$wgHooks['UnitTestsList'][] = 'efDonationInterfaceUnitTests'; |
| 405 | + |
| 406 | +//Stomp hooks |
| 407 | +if ($optionalParts['Stomp'] === true){ |
| 408 | + $wgHooks['ParserFirstCallInit'][] = 'efStompSetup'; |
| 409 | + $wgHooks['gwStomp'][] = 'sendSTOMP'; |
| 410 | + $wgHooks['gwPendingStomp'][] = 'sendPendingSTOMP'; |
| 411 | +} |
| 412 | + |
| 413 | +//Custom Filters hooks |
| 414 | +if ($optionalParts['CustomFilters'] === true){ |
| 415 | + $wgHooks["GatewayValidate"][] = array( 'Gateway_Extras_CustomFilters::onValidate' ); |
| 416 | +} |
| 417 | + |
| 418 | +//Conversion Log hooks |
| 419 | +if ($optionalParts['ConversionLog'] === true){ |
| 420 | + // Sets the 'conversion log' as logger for post-processing |
| 421 | + $wgHooks["GatewayPostProcess"][] = array( "Gateway_Extras_ConversionLog::onPostProcess" ); |
| 422 | +} |
| 423 | + |
| 424 | +//Recaptcha hooks |
| 425 | +if ($optionalParts['Recaptcha'] === true){ |
| 426 | + // Set reCpatcha as plugin for 'challenge' action |
| 427 | + $wgHooks["GatewayChallenge"][] = array( "Gateway_Extras_ReCaptcha::onChallenge" ); |
| 428 | +} |
| 429 | + |
| 430 | +/** |
| 431 | + * APIS |
| 432 | + */ |
| 433 | +// enable the API |
| 434 | +$wgAPIModules['donate'] = 'DonationApi'; |
| 435 | +$wgAutoloadClasses['DonationApi'] = $donationinterface_dir . 'gateway_common/donation.api.php'; |
| 436 | + |
| 437 | +//Payflowpro API |
| 438 | +if ( $optionalParts['PayflowPro'] === true ){ |
| 439 | + $wgAPIModules['pfp'] = 'ApiPayflowProGateway'; |
| 440 | + $wgAutoloadClasses['ApiPayflowProGateway'] = $donationinterface_dir . 'payflowpro_gateway/api_payflowpro_gateway.php'; |
| 441 | +} |
| 442 | + |
| 443 | + |
| 444 | +/** |
| 445 | + * ADDITIONAL MAGICAL GLOBALS |
| 446 | + */ |
| 447 | + |
| 448 | +// Resource modules |
| 449 | +$wgResourceTemplate = array( |
| 450 | + 'localBasePath' => $donationinterface_dir . 'modules', |
| 451 | + 'remoteExtPath' => 'DonationInterface/modules', |
| 452 | +); |
| 453 | +$wgResourceModules['iframe.liberator'] = array( |
| 454 | + 'scripts' => 'iframe.liberator.js', |
| 455 | + 'position' => 'top' |
| 456 | + ) + $wgResourceTemplate; |
| 457 | +$wgResourceModules['donationInterface.skinOverride'] = array( |
| 458 | + 'styles' => 'skinOverride.css', |
| 459 | + 'position' => 'top' |
| 460 | + ) + $wgResourceTemplate; |
| 461 | + |
| 462 | +$wgExtensionMessagesFiles['DonateInterface'] = $donationinterface_dir . 'donate_interface/donate_interface.i18n.php'; |
| 463 | + |
| 464 | + |
| 465 | +//GlobalCollect gateway magical globals |
| 466 | + |
| 467 | +//TODO: all the bits where we make the i18n make sense for multiple gateways. This is clearly less than ideal. |
| 468 | +if ( $optionalParts['GlobalCollect'] === true ){ |
| 469 | + $wgExtensionMessagesFiles['GlobalCollectGateway'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.i18n.php'; |
| 470 | + $wgExtensionMessagesFiles['GlobalCollectGatewayCountries'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.countries.i18n.php'; |
| 471 | + $wgExtensionMessagesFiles['GlobalCollectGatewayUSStates'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.us-states.i18n.php'; |
| 472 | + $wgExtensionAliasesFiles['GlobalCollectGateway'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.alias.php'; |
| 473 | +} |
| 474 | + |
| 475 | +//PayflowPro gateway magical globals |
| 476 | +if ( $optionalParts['PayflowPro'] === true ){ |
| 477 | + $wgExtensionMessagesFiles['PayflowProGateway'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.i18n.php'; |
| 478 | + $wgExtensionMessagesFiles['PayflowProGatewayCountries'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.countries.i18n.php'; |
| 479 | + $wgExtensionMessagesFiles['PayflowProGatewayUSStates'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.us-states.i18n.php'; |
| 480 | + $wgExtensionAliasesFiles['PayflowProGateway'] = $donationinterface_dir . 'payflowpro_gateway/payflowpro_gateway.alias.php'; |
| 481 | + $wgAjaxExportList[] = "fnPayflowProofofWork"; |
| 482 | +} |
| 483 | + |
| 484 | +//Minfraud magical globals |
| 485 | +if ( $optionalParts['Minfraud'] === true ){ //We do not want this in filter mode. |
| 486 | + $wgExtensionFunctions[] = 'efMinFraudSetup'; |
| 487 | +} |
| 488 | + |
| 489 | +//Minfraud as Filter globals |
| 490 | +if ( $optionalParts['Minfraud_as_filter'] === true ){ |
| 491 | + $wgExtensionFunctions[] = 'efCustomFiltersMinFraudSetup'; |
| 492 | +} |
| 493 | + |
| 494 | + |
| 495 | +/** |
| 496 | + * FUNCTIONS |
| 497 | + */ |
| 498 | + |
| 499 | +//---Stomp functions--- |
| 500 | +if ($optionalParts['Stomp'] === true){ |
| 501 | + require_once( $donationinterface_dir . 'activemq_stomp/activemq_stomp.php' ); |
| 502 | +} |
| 503 | + |
| 504 | +//---Minfraud functions--- |
| 505 | +if ($optionalParts['Minfraud'] === true){ |
| 506 | + require_once( $donationinterface_dir . 'extras/minfraud/minfraud.php' ); |
| 507 | +} |
| 508 | + |
| 509 | +//---Minfraud as filter functions--- |
| 510 | +if ($optionalParts['Minfraud_as_filter'] === true){ |
| 511 | + require_once( $donationinterface_dir . 'extras/custom_filters/filters/minfraud/minfraud.php' ); |
| 512 | +} |
| 513 | + |
| 514 | +function efDonationInterfaceUnitTests( &$files ) { |
| 515 | + $files[] = dirname( __FILE__ ) . '/tests/AllTests.php'; |
| 516 | + return true; |
| 517 | +} |
| 518 | + |
| 519 | +unset( $optionalParts ); |
Property changes on: trunk/extensions/DonationInterface/donationinterface.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 520 | + native |
Index: trunk/extensions/DonationInterface/tests/TestConfiguration.php.dist |
— | — | @@ -0,0 +1,52 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/* |
| 23 | + * This file contains custom options and constants for test configuration. |
| 24 | + */ |
| 25 | + |
| 26 | +/** |
| 27 | + * TESTS_MESSAGE_NOT_IMPLEMENTED |
| 28 | + * |
| 29 | + * Message for code that has not been implemented. |
| 30 | + */ |
| 31 | +define( 'TESTS_MESSAGE_NOT_IMPLEMENTED', 'Not implemented yet!' ); |
| 32 | + |
| 33 | +/** |
| 34 | + * TESTS_HOSTNAME |
| 35 | + * |
| 36 | + * The hostname for the system |
| 37 | + */ |
| 38 | +define( 'TESTS_HOSTNAME', 'localhost' ); |
| 39 | + |
| 40 | +/** |
| 41 | + * TESTS_EMAIL |
| 42 | + * |
| 43 | + * An email address to use in case test send mail |
| 44 | + */ |
| 45 | +define( 'TESTS_EMAIL', 'no-reply@wikimedia.org' ); |
| 46 | + |
| 47 | +/** |
| 48 | + * TESTS_PFP_CREDIT_CARDS_AMEREICAN_EXPRESS_VALID_CARD |
| 49 | + * |
| 50 | + * A "valid" test American Express Card for PayFlowPro. |
| 51 | + */ |
| 52 | +define( 'TESTS_PFP_CREDIT_CARDS_AMEREICAN_EXPRESS_VALID_CARD', '378282246310005' ); |
| 53 | + |
Index: trunk/extensions/DonationInterface/tests/Adapter/AllTests.php |
— | — | @@ -0,0 +1,61 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * |
| 19 | + * @since r98249 |
| 20 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 21 | + */ |
| 22 | + |
| 23 | +/** |
| 24 | + * @see DonationInterface_Adapter_ServerTestCase |
| 25 | + */ |
| 26 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'GatewayAdapterTestCase.php'; |
| 27 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'GlobalCollect/AllTests.php'; |
| 28 | + |
| 29 | +/** |
| 30 | + * AllTests |
| 31 | + */ |
| 32 | +class DonationInterface_Adapter_AllTests |
| 33 | +{ |
| 34 | + |
| 35 | + /** |
| 36 | + * Run the main test and load any parameters if needed. |
| 37 | + * |
| 38 | + */ |
| 39 | + public static function main() |
| 40 | + { |
| 41 | + $parameters = array(); |
| 42 | + |
| 43 | + PHPUnit_TextUI_TestRunner::run( self::suite(), $parameters ); |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Regular suite |
| 48 | + * |
| 49 | + * All tests except those that require output buffering. |
| 50 | + * |
| 51 | + * @return PHPUnit_Framework_TestSuite |
| 52 | + */ |
| 53 | + public static function suite() |
| 54 | + { |
| 55 | + $suite = new PHPUnit_Framework_TestSuite( 'Donation Interface - Adapter Suite' ); |
| 56 | + |
| 57 | + $suite->addTestSuite( 'DonationInterface_Adapter_GatewayAdapterTestCase' ); |
| 58 | + $suite->addTestSuite( 'DonationInterface_Adapter_GlobalCollect_AllTests' ); |
| 59 | + |
| 60 | + return $suite; |
| 61 | + } |
| 62 | +} |
Property changes on: trunk/extensions/DonationInterface/tests/Adapter/AllTests.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 63 | + text/plain |
Added: svn:keywords |
2 | 64 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 65 | + native |
Index: trunk/extensions/DonationInterface/tests/Adapter/GatewayAdapterTestCase.php |
— | — | @@ -0,0 +1,331 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Katie Horn <khorn@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see DonationInterfaceTestCase |
| 24 | + */ |
| 25 | +require_once dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'DonationInterfaceTestCase.php'; |
| 26 | + |
| 27 | +/** |
| 28 | + * TODO: Test everything. |
| 29 | + * Make sure all the basic functions in the gateway_adapter are tested here. |
| 30 | + * Also, the extras and their hooks firing properly and... that the fail score |
| 31 | + * they give back is acted upon in the way we think it does. |
| 32 | + * Hint: For that mess, use GatewayAdapter's $debugarray |
| 33 | + * |
| 34 | + * Also, note that it barely makes sense to test the functions that need to be |
| 35 | + * defined in each gateway as per the abstract class. If we did that here, we'd |
| 36 | + * basically be just testing the test code. So, don't do it. |
| 37 | + * Those should definitely be tested in the various gateway-specific test |
| 38 | + * classes. |
| 39 | + * |
| 40 | + * @group Fundraising |
| 41 | + * @group Splunge |
| 42 | + * @group Gateways |
| 43 | + * @group DonationInterface |
| 44 | + */ |
| 45 | +class DonationInterface_Adapter_GatewayAdapterTestCase extends DonationInterfaceTestCase { |
| 46 | + |
| 47 | + /** |
| 48 | + * |
| 49 | + * @covers GatewayAdapter::__construct |
| 50 | + * @covers GatewayAdapter::defineVarMap |
| 51 | + * @covers GatewayAdapter::defineReturnValueMap |
| 52 | + * @covers GatewayAdapter::defineTransactions |
| 53 | + */ |
| 54 | + public function testbuildRequestXML() { |
| 55 | + $gateway = new TestAdapter(); |
| 56 | + $gateway->publicCurrentTransaction( 'Test1' ); |
| 57 | + $built = $gateway->buildRequestXML(); |
| 58 | + $expected = '<?xml version="1.0"?>' . "\n"; |
| 59 | + $expected .= '<XML><REQUEST><ACTION>Donate</ACTION><ACCOUNT><MERCHANTID>128</MERCHANTID><PASSWORD>k4ftw</PASSWORD><VERSION>3.2</VERSION><RETURNURL>http://' . TESTS_HOSTNAME . '/index.php/Special:GlobalCollectGatewayResult</RETURNURL></ACCOUNT><DONATION><DONOR>Tester Testington</DONOR><AMOUNT>35000</AMOUNT><CURRENCYCODE>USD</CURRENCYCODE><LANGUAGECODE>en</LANGUAGECODE><COUNTRYCODE>US</COUNTRYCODE></DONATION></REQUEST></XML>' . "\n"; |
| 60 | + $this->assertEquals($built, $expected, "The constructed XML for transaction type Test1 does not match our expected."); |
| 61 | + |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * |
| 66 | + */ |
| 67 | + public function testParseResponseStatusXML() { |
| 68 | + |
| 69 | + $returned = $this->getTestGatewayTransactionTest2Results(); |
| 70 | + $this->assertEquals($returned['status'], true, "Status should be true at this point."); |
| 71 | + } |
| 72 | + |
| 73 | + /** |
| 74 | + * |
| 75 | + */ |
| 76 | + public function testParseResponseErrorsXML() { |
| 77 | + |
| 78 | + $returned = $this->getTestGatewayTransactionTest2Results(); |
| 79 | + $expected_errors = array( |
| 80 | + 128 => "Your shoe's untied...", |
| 81 | + 45 => "Low clearance!" |
| 82 | + ); |
| 83 | + $this->assertEquals($returned['errors'], $expected_errors, "Expected errors were not found."); |
| 84 | + |
| 85 | + } |
| 86 | + |
| 87 | + /** |
| 88 | + * |
| 89 | + */ |
| 90 | + public function testParseResponseDataXML() { |
| 91 | + |
| 92 | + $returned = $this->getTestGatewayTransactionTest2Results(); |
| 93 | + $expected_data = array( |
| 94 | + 'thing' => 'stuff', |
| 95 | + 'otherthing' => 12, |
| 96 | + ); |
| 97 | + $this->assertEquals($returned['data'], $expected_data, "Expected data was not found."); |
| 98 | + |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * |
| 103 | + */ |
| 104 | + public function testResponseMessage() { |
| 105 | + |
| 106 | + $returned = $this->getTestGatewayTransactionTest2Results(); |
| 107 | + $this->assertEquals($returned['message'], "Test2 Transaction Successful!", "Expected message was not returned."); |
| 108 | + |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * |
| 113 | + */ |
| 114 | + public function testGetGlobal(){ |
| 115 | + $gateway = new TestAdapter(); |
| 116 | + $found = $gateway::getGlobal("TestVar"); |
| 117 | + $expected = "Hi there!"; |
| 118 | + $this->assertEquals($found, $expected, "getGlobal is not functioning properly."); |
| 119 | + } |
| 120 | + |
| 121 | + |
| 122 | + /** |
| 123 | + * |
| 124 | + */ |
| 125 | + public function getTestGatewayTransactionTest2Results(){ |
| 126 | + $gateway = new TestAdapter(); |
| 127 | + return $gateway->do_transaction( 'Test2' ); |
| 128 | + } |
| 129 | + |
| 130 | +} |
| 131 | + |
| 132 | +/** |
| 133 | + * Test Adapter |
| 134 | + */ |
| 135 | +class TestAdapter extends GatewayAdapter { |
| 136 | + |
| 137 | + const GATEWAY_NAME = 'Test Gateway'; |
| 138 | + const IDENTIFIER = 'testgateway'; |
| 139 | + const COMMUNICATION_TYPE = 'xml'; |
| 140 | + const GLOBAL_PREFIX = 'wgTestAdapterGateway'; |
| 141 | + |
| 142 | + /** |
| 143 | + * |
| 144 | + */ |
| 145 | + public function stageData( $type = 'request' ){ |
| 146 | + $this->postdata['amount'] = $this->postdata['amount'] * 1000; |
| 147 | + $this->postdata['name'] = $this->postdata['fname'] . " " . $this->postdata['lname']; |
| 148 | + } |
| 149 | + |
| 150 | + /** |
| 151 | + * |
| 152 | + */ |
| 153 | + public function __construct( ) { |
| 154 | + global $wgTestAdapterGatewayTestVar, $wgTestAdapterGatewayUseSyslog, $wgTestAdapterGatewayTest; |
| 155 | + $wgTestAdapterGatewayTest = true; |
| 156 | + $wgTestAdapterGatewayTestVar = "Hi there!"; |
| 157 | + $wgTestAdapterGatewayUseSyslog = true; |
| 158 | + parent::__construct(); |
| 159 | + |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * |
| 164 | + */ |
| 165 | + public function defineAccountInfo(){ |
| 166 | + $this->accountInfo = array( |
| 167 | + 'MERCHANTID' => '128', |
| 168 | + 'PASSWORD' => 'k4ftw', |
| 169 | + //'IPADDRESS' => '', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm. |
| 170 | + 'VERSION' => "3.2", |
| 171 | + ); |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * |
| 176 | + */ |
| 177 | + public function defineStagedVars(){ |
| 178 | + } |
| 179 | + |
| 180 | + /** |
| 181 | + * |
| 182 | + */ |
| 183 | + public function defineVarMap(){ |
| 184 | + $this->var_map = array( |
| 185 | + 'DONOR' => 'name', |
| 186 | + 'AMOUNT' => 'amount', |
| 187 | + 'CURRENCYCODE' => 'currency', |
| 188 | + 'LANGUAGECODE' => 'language', |
| 189 | + 'COUNTRYCODE' => 'country', |
| 190 | + 'OID' => 'order_id', |
| 191 | + 'RETURNURL' => 'returnto', //TODO: Fund out where the returnto URL is supposed to be coming from. |
| 192 | + ); |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * |
| 197 | + */ |
| 198 | + public function defineReturnValueMap(){ |
| 199 | + $this->return_value_map = array( |
| 200 | + 'AOK' => true, |
| 201 | + 'WRONG' => false, |
| 202 | + ); |
| 203 | + } |
| 204 | + |
| 205 | + /** |
| 206 | + * |
| 207 | + */ |
| 208 | + public function defineTransactions(){ |
| 209 | + $this->transactions = array(); |
| 210 | + |
| 211 | + $this->transactions['Test1'] = array( |
| 212 | + 'request' => array( |
| 213 | + 'REQUEST' => array( |
| 214 | + 'ACTION', |
| 215 | + 'ACCOUNT' => array( |
| 216 | + 'MERCHANTID', |
| 217 | + 'PASSWORD', |
| 218 | + 'VERSION', |
| 219 | + 'RETURNURL', |
| 220 | + ), |
| 221 | + 'DONATION' => array( |
| 222 | + 'DONOR', |
| 223 | + 'AMOUNT', |
| 224 | + 'CURRENCYCODE', |
| 225 | + 'LANGUAGECODE', |
| 226 | + 'COUNTRYCODE', |
| 227 | + //'OID', //move this to another test. It's different every time, dorkus. |
| 228 | + ) |
| 229 | + ) |
| 230 | + ), |
| 231 | + 'values' => array( |
| 232 | + 'ACTION' => 'Donate', |
| 233 | + ), |
| 234 | + ); |
| 235 | + |
| 236 | + $this->transactions['Test2'] = array( |
| 237 | + 'request' => array( |
| 238 | + 'REQUEST' => array( |
| 239 | + 'ACTION', |
| 240 | + ) |
| 241 | + ), |
| 242 | + 'values' => array( |
| 243 | + 'ACTION' => 'Donate2', |
| 244 | + ), |
| 245 | + ); |
| 246 | + } |
| 247 | + |
| 248 | + /** |
| 249 | + * Take the entire response string, and strip everything we don't care about. |
| 250 | + * For instance: If it's XML, we only want correctly-formatted XML. Headers must be killed off. |
| 251 | + * return a string. |
| 252 | + */ |
| 253 | + public function getFormattedResponse( $rawResponse ){ |
| 254 | + $xmlString = $this->stripXMLResponseHeaders($rawResponse); |
| 255 | + $displayXML = $this->formatXmlString( $xmlString ); |
| 256 | + $realXML = new DomDocument( '1.0' ); |
| 257 | + self::log( "Here is the Raw XML: " . $displayXML ); //I am apparently a huge fibber. |
| 258 | + $realXML->loadXML( trim( $xmlString ) ); |
| 259 | + return $realXML; |
| 260 | + } |
| 261 | + |
| 262 | + /** |
| 263 | + * Parse the response to get the status. Not sure if this should return a bool, or something more... telling. |
| 264 | + */ |
| 265 | + public function getResponseStatus( $response ){ |
| 266 | + |
| 267 | + $aok = true; |
| 268 | + |
| 269 | + foreach ( $response->getElementsByTagName( 'RESULT' ) as $node ) { |
| 270 | + if ( array_key_exists( $node->nodeValue, $this->return_value_map ) && $this->return_value_map[$node->nodeValue] !== true ) { |
| 271 | + $aok = false; |
| 272 | + } |
| 273 | + } |
| 274 | + |
| 275 | + return $aok; |
| 276 | + } |
| 277 | + |
| 278 | + /** |
| 279 | + * Parse the response to get the errors in a format we can log and otherwise deal with. |
| 280 | + * return a key/value array of codes (if they exist) and messages. |
| 281 | + */ |
| 282 | + public function getResponseErrors( $response ){ |
| 283 | + $errors = array(); |
| 284 | + foreach ( $response->getElementsByTagName( 'warning' ) as $node ) { |
| 285 | + $code = ''; |
| 286 | + $message = ''; |
| 287 | + foreach ( $node->childNodes as $childnode ) { |
| 288 | + if ($childnode->nodeName === "code"){ |
| 289 | + $code = $childnode->nodeValue; |
| 290 | + } |
| 291 | + if ($childnode->nodeName === "message"){ |
| 292 | + $message = $childnode->nodeValue; |
| 293 | + } |
| 294 | + } |
| 295 | + $errors[$code] = $message; |
| 296 | + } |
| 297 | + return $errors; |
| 298 | + } |
| 299 | + |
| 300 | + /** |
| 301 | + * Harvest the data we need back from the gateway. |
| 302 | + * return a key/value array |
| 303 | + */ |
| 304 | + public function getResponseData( $response ){ |
| 305 | + $data = array(); |
| 306 | + foreach ( $response->getElementsByTagName( 'ImportantData' ) as $node ) { |
| 307 | + foreach ( $node->childNodes as $childnode ) { |
| 308 | + if (trim($childnode->nodeValue) != ''){ |
| 309 | + $data[$childnode->nodeName] = $childnode->nodeValue; |
| 310 | + } |
| 311 | + } |
| 312 | + } |
| 313 | + self::log( "Returned Data: " . print_r($data, true)); |
| 314 | + return $data; |
| 315 | + } |
| 316 | + |
| 317 | + public function processResponse( $response ) { |
| 318 | + //TODO: Stuff. |
| 319 | + } |
| 320 | + |
| 321 | + public function publicCurrentTransaction( $transaction = '' ){ |
| 322 | + $this->currentTransaction( $transaction ); |
| 323 | + } |
| 324 | + |
| 325 | + public function curl_transaction($data) { |
| 326 | + $data = ""; |
| 327 | + $data['result'] = 'BLAH BLAH BLAH BLAH whatever something blah blah<?xml version="1.0"?>' . "\n" . '<XML><Response><Status>AOK</Status><ImportantData><thing>stuff</thing><otherthing>12</otherthing></ImportantData><errorswarnings><warning><code>128</code><message>Your shoe\'s untied...</message></warning><warning><code>45</code><message>Low clearance!</message></warning></errorswarnings></Response></XML>'; |
| 328 | + $this->setTransactionResult( $data ); |
| 329 | + return true; |
| 330 | + } |
| 331 | +} |
| 332 | + |
Property changes on: trunk/extensions/DonationInterface/tests/Adapter/GatewayAdapterTestCase.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 333 | + native |
Index: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/AllTests.php |
— | — | @@ -0,0 +1,61 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * |
| 19 | + * @since r98249 |
| 20 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 21 | + */ |
| 22 | + |
| 23 | +/** |
| 24 | + * @see DonationInterface_Adapter_ServerTestCase |
| 25 | + */ |
| 26 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'BankTransferTestCase.php'; |
| 27 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'GlobalCollectTestCase.php'; |
| 28 | + |
| 29 | +/** |
| 30 | + * AllTests |
| 31 | + */ |
| 32 | +class DonationInterface_Adapter_GlobalCollect_AllTests |
| 33 | +{ |
| 34 | + |
| 35 | + /** |
| 36 | + * Run the main test and load any parameters if needed. |
| 37 | + * |
| 38 | + */ |
| 39 | + public static function main() |
| 40 | + { |
| 41 | + $parameters = array(); |
| 42 | + |
| 43 | + PHPUnit_TextUI_TestRunner::run( self::suite(), $parameters ); |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Regular suite |
| 48 | + * |
| 49 | + * All tests except those that require output buffering. |
| 50 | + * |
| 51 | + * @return PHPUnit_Framework_TestSuite |
| 52 | + */ |
| 53 | + public static function suite() |
| 54 | + { |
| 55 | + $suite = new PHPUnit_Framework_TestSuite( 'Donation Interface - Adapter Suite' ); |
| 56 | + |
| 57 | + $suite->addTestSuite( 'DonationInterface_Adapter_GlobalCollect_BankTransferTestCase' ); |
| 58 | + $suite->addTestSuite( 'DonationInterface_Adapter_GlobalCollect_GlobalCollectTestCase' ); |
| 59 | + |
| 60 | + return $suite; |
| 61 | + } |
| 62 | +} |
Property changes on: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/AllTests.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 63 | + text/plain |
Added: svn:keywords |
2 | 64 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 65 | + native |
Index: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/BankTransferTestCase.php |
— | — | @@ -0,0 +1,193 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see DonationInterfaceTestCase |
| 24 | + */ |
| 25 | +require_once dirname( dirname( dirname( __FILE__ ) ) ) . DIRECTORY_SEPARATOR . 'DonationInterfaceTestCase.php'; |
| 26 | + |
| 27 | +/** |
| 28 | + * |
| 29 | + * @group Fundraising |
| 30 | + * @group Gateways |
| 31 | + * @group DonationInterface |
| 32 | + * @group GlobalCollect |
| 33 | + * @group BankTransfer |
| 34 | + */ |
| 35 | +class DonationInterface_Adapter_GlobalCollect_BankTransferTestCase extends DonationInterfaceTestCase { |
| 36 | + |
| 37 | + /* |
| 38 | + Acceptance Criteria |
| 39 | + 308.1 *Given that a donor wants to donate via an offline bank transfer |
| 40 | + When they submit the following information: |
| 41 | + Amount |
| 42 | + Country (Required – use ISO 3166 codes) |
| 43 | + First Name |
| 44 | + Surname |
| 45 | + Street Address |
| 46 | + Zip |
| 47 | + City |
| 48 | + State (optional) |
| 49 | + Then an XML submission is created and sent to Global Collect with the above information AND: |
| 50 | + MERCHANTREFERENCE (contribution tracking id) |
| 51 | + curencycode |
| 52 | + 308.2 |
| 53 | + GIven that a donation order was submitted with the above information |
| 54 | + When the submit button is pressed and a response recieved from Global Collect |
| 55 | + The response is parased and the following information is displayed to the donor: |
| 56 | + PAYMENTREFERENCE |
| 57 | + Account Holder |
| 58 | + Bank Name |
| 59 | + City |
| 60 | + Swift Code |
| 61 | + SpecialID (if provided) |
| 62 | + Bank Account Number |
| 63 | + IBAN |
| 64 | + CountryDescription |
| 65 | + Notes |
| 66 | + We do not need to have donor information stored on our side yet as long as it is sent to Global Collect |
| 67 | + */ |
| 68 | + |
| 69 | + /** |
| 70 | + * Copied from Katie's test code |
| 71 | + */ |
| 72 | + public function testbuildRequestXML() { |
| 73 | + |
| 74 | + $this->markTestIncomplete( TESTS_MESSAGE_NOT_IMPLEMENTED ); |
| 75 | + |
| 76 | + return; |
| 77 | + |
| 78 | + $gateway = new TestAdapter(); |
| 79 | + $gateway->publicCurrentTransaction( 'Test1' ); |
| 80 | + $built = $gateway->buildRequestXML(); |
| 81 | + $expected = '<?xml version="1.0"?>' . "\n"; |
| 82 | + $expected .= '<XML><REQUEST><ACTION>Donate</ACTION><ACCOUNT><MERCHANTID>128</MERCHANTID><PASSWORD>k4ftw</PASSWORD><VERSION>3.2</VERSION><RETURNURL>http://' . TESTS_HOSTNAME . '/index.php/Donate-thanks/en</RETURNURL></ACCOUNT><DONATION><DONOR>Tester Testington</DONOR><AMOUNT>35000</AMOUNT><CURRENCYCODE>USD</CURRENCYCODE><LANGUAGECODE>en</LANGUAGECODE><COUNTRYCODE>US</COUNTRYCODE></DONATION></REQUEST></XML>' . "\n"; |
| 83 | + $this->assertEquals($built, $expected, "The constructed XML for transaction type Test1 does not match our expected."); |
| 84 | + |
| 85 | + } |
| 86 | + |
| 87 | + |
| 88 | + /** |
| 89 | + * testRequestHasRequiredFields |
| 90 | + */ |
| 91 | + public function testRequestHasRequiredFields() { |
| 92 | + |
| 93 | + $this->markTestIncomplete( TESTS_MESSAGE_NOT_IMPLEMENTED ); |
| 94 | + |
| 95 | + } |
| 96 | + |
| 97 | + /** |
| 98 | + * testReturnDonorResponse |
| 99 | + */ |
| 100 | + public function testReturnDonorResponse() { |
| 101 | + |
| 102 | + $this->markTestIncomplete( TESTS_MESSAGE_NOT_IMPLEMENTED ); |
| 103 | + |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * testSendToGlobalCollect |
| 108 | + * |
| 109 | + * Adding |
| 110 | + */ |
| 111 | + public function testSendToGlobalCollect() { |
| 112 | + $this->markTestIncomplete( TESTS_MESSAGE_NOT_IMPLEMENTED ); |
| 113 | + |
| 114 | + //global $wgContLang, $wgAuth, $wgMemc, $wgRequest, $wgUser, $wgServer; |
| 115 | + global $wgGlobalCollectGatewayTest; |
| 116 | + |
| 117 | + $wgGlobalCollectGatewayTest = true; |
| 118 | + |
| 119 | + $_SERVER = array(); |
| 120 | + |
| 121 | + $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; |
| 122 | + $_SERVER['HTTP_HOST'] = TESTS_HOSTNAME; |
| 123 | + $_SERVER['SERVER_NAME'] = TESTS_HOSTNAME; |
| 124 | + $_SERVER['REQUEST_URI'] = '/index.php/Special:GlobalCollectGateway?form_name=TwoStepAmount'; |
| 125 | + |
| 126 | + /** |
| 127 | + * @see WebStart.php |
| 128 | + */ |
| 129 | + require_once TESTS_WEB_ROOT . '/includes/WebStart.php'; |
| 130 | + |
| 131 | + $options = array(); |
| 132 | + |
| 133 | + $options['test'] = true; |
| 134 | + $options['transactionType'] = 'BANK_TRANSFER'; |
| 135 | + |
| 136 | + $options['postDefaults'] = array( |
| 137 | + 'returnTitle' => true, |
| 138 | + 'returnTo' => 'http://' . TESTS_HOSTNAME . '/index.php/Special:GlobalCollectGatewayResult', |
| 139 | + ); |
| 140 | + |
| 141 | + $options['testData'] = array( |
| 142 | + 'amount' => "35", |
| 143 | + 'amountOther' => '', |
| 144 | + 'email' => 'test@example.com', |
| 145 | + 'fname' => 'Tester', |
| 146 | + 'mname' => 'T.', |
| 147 | + 'lname' => 'Testington', |
| 148 | + 'street' => '548 Market St.', |
| 149 | + 'city' => 'San Francisco', |
| 150 | + 'state' => 'CA', |
| 151 | + 'zip' => '94104', |
| 152 | + 'country' => 'US', |
| 153 | + 'fname2' => 'Testy', |
| 154 | + 'lname2' => 'Testerson', |
| 155 | + 'street2' => '123 Telegraph Ave.', |
| 156 | + 'city2' => 'Berkeley', |
| 157 | + 'state2' => 'CA', |
| 158 | + 'zip2' => '94703', |
| 159 | + 'country2' => 'US', |
| 160 | + 'size' => 'small', |
| 161 | + 'premium_language' => 'es', |
| 162 | + //'card_num' => TESTS_CREDIT_CARDS_AMEREICAN_EXPRESS_VALID_CARD, |
| 163 | + //'card_type' => 'american', |
| 164 | + //'expiration' => date( 'my', strtotime( '+1 year 1 month' ) ), |
| 165 | + //'cvv' => '001', |
| 166 | + 'currency' => 'USD', |
| 167 | + 'payment_method' => '', |
| 168 | + 'order_id' => '1234567890', |
| 169 | + 'i_order_id' => '1234567890', |
| 170 | + 'numAttempt' => 0, |
| 171 | + 'referrer' => 'http://' . TESTS_HOSTNAME . '/index.php/Special:GlobalCollectGateway?form_name=TwoStepAmount', |
| 172 | + 'utm_source' => '..gc_bt', |
| 173 | + 'utm_medium' => null, |
| 174 | + 'utm_campaign' => null, |
| 175 | + 'language' => 'en', |
| 176 | + 'comment-option' => '', |
| 177 | + 'comment' => '', |
| 178 | + 'email-opt' => 1, |
| 179 | + 'test_string' => '', |
| 180 | + 'token' => '', |
| 181 | + 'contribution_tracking_id' => '', |
| 182 | + 'data_hash' => '', |
| 183 | + 'action' => '', |
| 184 | + 'gateway' => 'globalcollect', |
| 185 | + 'owa_session' => '', |
| 186 | + 'owa_ref' => 'http://localhost/defaultTestData', |
| 187 | + 'transaction_type' => '', // Used by GlobalCollect for payment types |
| 188 | + ); |
| 189 | + |
| 190 | + $gateway = new GlobalCollectAdapter( $options ); |
| 191 | + return $gateway->do_transaction( $options['transactionType'] ); |
| 192 | + } |
| 193 | +} |
| 194 | + |
Property changes on: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/BankTransferTestCase.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 195 | + text/plain |
Added: svn:keywords |
2 | 196 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 197 | + native |
Index: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/GlobalCollectTestCase.php |
— | — | @@ -0,0 +1,71 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see DonationInterfaceTestCase |
| 24 | + */ |
| 25 | +require_once dirname( dirname( dirname( __FILE__ ) ) ) . DIRECTORY_SEPARATOR . 'DonationInterfaceTestCase.php'; |
| 26 | + |
| 27 | +/** |
| 28 | + * |
| 29 | + * @group Fundraising |
| 30 | + * @group Gateways |
| 31 | + * @group DonationInterface |
| 32 | + * @group GlobalCollect |
| 33 | + */ |
| 34 | +class DonationInterface_Adapter_GlobalCollect_GlobalCollectTestCase extends DonationInterfaceTestCase { |
| 35 | + |
| 36 | + |
| 37 | + /** |
| 38 | + * testDefineVarMap |
| 39 | + * @covers GlobalCollectAdapter::__construct |
| 40 | + * @covers GlobalCollectAdapter::defineVarMap |
| 41 | + */ |
| 42 | + public function testDefineVarMap() { |
| 43 | + |
| 44 | + $adapter = new GlobalCollectAdapter(); |
| 45 | + |
| 46 | + $var_map = array( |
| 47 | + 'ORDERID' => 'order_id', |
| 48 | + 'AMOUNT' => 'amount', |
| 49 | + 'CURRENCYCODE' => 'currency', |
| 50 | + 'LANGUAGECODE' => 'language', |
| 51 | + 'COUNTRYCODE' => 'country', |
| 52 | + 'MERCHANTREFERENCE' => 'order_id', |
| 53 | + 'RETURNURL' => 'returnto', //TODO: Fund out where the returnto URL is supposed to be coming from. |
| 54 | + 'IPADDRESS' => 'user_ip', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm. |
| 55 | + 'PAYMENTPRODUCTID' => 'card_type', |
| 56 | + 'CVV' => 'cvv', |
| 57 | + 'EXPIRYDATE' => 'expiration', |
| 58 | + 'CREDITCARDNUMBER' => 'card_num', |
| 59 | + 'FIRSTNAME' => 'fname', |
| 60 | + 'SURNAME' => 'lname', |
| 61 | + 'STREET' => 'street', |
| 62 | + 'CITY' => 'city', |
| 63 | + 'STATE' => 'state', |
| 64 | + 'ZIP' => 'zip', |
| 65 | + 'EMAIL' => 'email', |
| 66 | + ); |
| 67 | + |
| 68 | + $this->assertEquals( $var_map, $adapter->var_map ); |
| 69 | + |
| 70 | + } |
| 71 | +} |
| 72 | + |
Property changes on: trunk/extensions/DonationInterface/tests/Adapter/GlobalCollect/GlobalCollectTestCase.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 73 | + text/plain |
Added: svn:keywords |
2 | 74 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 75 | + native |
Index: trunk/extensions/DonationInterface/tests/AllTests.php |
— | — | @@ -0,0 +1,60 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see DonationInterface_Adapter_AllTests |
| 24 | + */ |
| 25 | +require_once 'Adapter/AllTests.php'; |
| 26 | +require_once 'DonationDataTestCase.php'; |
| 27 | + |
| 28 | +/** |
| 29 | + * AllTests |
| 30 | + */ |
| 31 | +class DonationInterface_AllTests |
| 32 | +{ |
| 33 | + |
| 34 | + /** |
| 35 | + * Run the main test and load any parameters if needed. |
| 36 | + * |
| 37 | + */ |
| 38 | + public static function main() |
| 39 | + { |
| 40 | + $parameters = array(); |
| 41 | + |
| 42 | + PHPUnit_TextUI_TestRunner::run( self::suite(), $parameters ); |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * Run test suites |
| 47 | + * |
| 48 | + * @return PHPUnit_Framework_TestSuite |
| 49 | + */ |
| 50 | + public static function suite() |
| 51 | + { |
| 52 | + $suite = new PHPUnit_Framework_TestSuite( 'Donation Interface Suite' ); |
| 53 | + |
| 54 | + $suite->addTestSuite( 'DonationInterface_Adapter_AllTests' ); |
| 55 | + $suite->addTestSuite( 'DonationInterface_DonationDataTestCase' ); |
| 56 | + //$suite->addTest(DonationInterface_Adapter_AllTests::suite()); |
| 57 | + |
| 58 | + return $suite; |
| 59 | + } |
| 60 | +} |
| 61 | + |
Property changes on: trunk/extensions/DonationInterface/tests/AllTests.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 62 | + text/plain |
Added: svn:keywords |
2 | 63 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 64 | + native |
Index: trunk/extensions/DonationInterface/tests/TestHelper.php |
— | — | @@ -0,0 +1,84 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * |
| 19 | + * @category UnitTesting |
| 20 | + * @package Fundraising_QueueHandling |
| 21 | + * @license http://www.gnu.org/copyleft/gpl.html GNU GENERAL PUBLIC LICENSE |
| 22 | + * @since r462 |
| 23 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 24 | + */ |
| 25 | + |
| 26 | +/* |
| 27 | + * Include PHPUnit dependencies |
| 28 | + */ |
| 29 | +require_once 'PHPUnit/Framework/IncompleteTestError.php'; |
| 30 | +require_once 'PHPUnit/Framework/TestCase.php'; |
| 31 | +require_once 'PHPUnit/Framework/TestSuite.php'; |
| 32 | +require_once 'PHPUnit/Runner/Version.php'; |
| 33 | +require_once 'PHPUnit/TextUI/TestRunner.php'; |
| 34 | +require_once 'PHPUnit/Util/Filter.php'; |
| 35 | + |
| 36 | +/* |
| 37 | + * Set error reporting to the level to which code must comply. |
| 38 | + */ |
| 39 | +error_reporting( E_ALL | E_STRICT ); |
| 40 | + |
| 41 | + |
| 42 | +/** |
| 43 | + * @see DonationData |
| 44 | + */ |
| 45 | +require_once dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'gateway_common/DonationData.php'; |
| 46 | + |
| 47 | +/** |
| 48 | + * @see GatewayAdapter |
| 49 | + */ |
| 50 | +require_once dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'gateway_common/gateway.adapter.php'; |
| 51 | + |
| 52 | +/** |
| 53 | + * @see GlobalCollectAdapter |
| 54 | + */ |
| 55 | +require_once dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'globalcollect_gateway/globalcollect.adapter.php'; |
| 56 | + |
| 57 | +/** |
| 58 | + * @see ContributionTrackingProcessor |
| 59 | + */ |
| 60 | +require_once dirname( dirname( dirname( __FILE__ ) ) ) . DIRECTORY_SEPARATOR . 'ContributionTracking/ContributionTracking.processor.php'; |
| 61 | + |
| 62 | +/** |
| 63 | + * TESTS_WEB_ROOT |
| 64 | + * |
| 65 | + * This is similar to $IP, the installation path in Mediawiki. |
| 66 | + */ |
| 67 | +define( 'TESTS_WEB_ROOT', dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ); |
| 68 | + |
| 69 | +/* |
| 70 | + * Unit tests are run from the command line. |
| 71 | + * |
| 72 | + * It should be confirmed that this will not affect other tests such as Selenium. |
| 73 | + */ |
| 74 | +$wgCommandLineMode = true; |
| 75 | + |
| 76 | +/* |
| 77 | + * Load the user-defined test configuration file, if it exists; otherwise, load |
| 78 | + * the default configuration. |
| 79 | + */ |
| 80 | +if ( is_file( 'TestConfiguration.php' ) ) { |
| 81 | + require_once 'TestConfiguration.php'; |
| 82 | +} else { |
| 83 | + require_once 'TestConfiguration.php.dist'; |
| 84 | +} |
| 85 | + |
Property changes on: trunk/extensions/DonationInterface/tests/TestHelper.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 86 | + text/plain |
Added: svn:keywords |
2 | 87 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 88 | + native |
Index: trunk/extensions/DonationInterface/tests/DonationDataTestCase.php |
— | — | @@ -0,0 +1,331 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Katie Horn <khorn@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see DonationInterfaceTestCase |
| 24 | + */ |
| 25 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'DonationInterfaceTestCase.php'; |
| 26 | + |
| 27 | +/** |
| 28 | + * @group Fundraising |
| 29 | + * @group Splunge |
| 30 | + * @group Gateways |
| 31 | + * @author Katie Horn <khorn@wikimedia.org> |
| 32 | + */ |
| 33 | +class DonationInterface_DonationDataTestCase extends DonationInterfaceTestCase { |
| 34 | + |
| 35 | + /** |
| 36 | + * |
| 37 | + */ |
| 38 | + public function __construct(){ |
| 39 | + parent::__construct(); |
| 40 | + $this->testData = array( |
| 41 | + 'amount' => '128', |
| 42 | + 'email' => 'unittest@example.com', |
| 43 | + 'fname' => 'Testocres', |
| 44 | + 'mname' => 'S.', |
| 45 | + 'lname' => 'McTestingyou', |
| 46 | + 'street' => '123 Fake Street', |
| 47 | + 'city' => 'Springfield', |
| 48 | + 'state' => 'US', |
| 49 | + 'zip' => '99999', |
| 50 | + 'country' => 'US', |
| 51 | + 'fname2' => 'Testor', |
| 52 | + 'lname2' => 'Testeyton', |
| 53 | + 'street2' => '123 W. Grand Ave.', |
| 54 | + 'city2' => 'Oakland', |
| 55 | + 'state2' => 'CA', |
| 56 | + 'zip2' => '94607', |
| 57 | + 'country2' => 'US', |
| 58 | + 'size' => 'Big mcLargeHuge', |
| 59 | + 'premium_language' => 'fr', |
| 60 | + 'card_num' => '42', |
| 61 | + 'card_type' => 'visa', |
| 62 | + 'expiration' => '1138', |
| 63 | + 'cvv' => '665', |
| 64 | + 'currency' => 'USD', |
| 65 | + 'payment_method' => '', |
| 66 | + 'i_order_id' => '1234567890', |
| 67 | + 'numAttempt' => '5', |
| 68 | + 'referrer' => 'http://www.testing.com/', |
| 69 | + 'utm_source' => '..dd', |
| 70 | + 'utm_medium' => 'large', |
| 71 | + 'utm_campaign' => 'yes', |
| 72 | + 'comment-option' => '', |
| 73 | + 'comment' => 'My hovercraft is full of eels', |
| 74 | + 'email-opt' => '', |
| 75 | + 'test_string' => '', |
| 76 | + 'token' => '113811', |
| 77 | + 'contribution_tracking_id' => '', |
| 78 | + 'data_hash' => '', |
| 79 | + 'action' => '', |
| 80 | + 'gateway' => 'chainlink', |
| 81 | + 'owa_session' => '', |
| 82 | + 'owa_ref' => 'http://localhost/importedTestData', |
| 83 | + ); |
| 84 | + |
| 85 | + } |
| 86 | + |
| 87 | + |
| 88 | + /** |
| 89 | + * @covers DonationData::__construct |
| 90 | + * @covers DonationData::getData |
| 91 | + * @covers DonationData::populateData |
| 92 | + * @covers DonationData::doCacheStuff |
| 93 | + * @covers DonationData::normalizeAndSanitize |
| 94 | + * @covers DonationData::getVal |
| 95 | + */ |
| 96 | + public function testConstruct(){ |
| 97 | + $ddObj = new DonationData(''); //as if we were posted. |
| 98 | + $returned = $ddObj->getData(); |
| 99 | + $expected = array( 'posted' => '', |
| 100 | + 'amount' => '0.00', |
| 101 | + 'email' => '', |
| 102 | + 'fname' => '', |
| 103 | + 'mname' => '', |
| 104 | + 'lname' => '', |
| 105 | + 'street' => '', |
| 106 | + 'city' => '', |
| 107 | + 'state' => '', |
| 108 | + 'zip' => '', |
| 109 | + 'country' => '', |
| 110 | + 'fname2' => '', |
| 111 | + 'lname2' => '', |
| 112 | + 'street2' => '', |
| 113 | + 'city2' => '', |
| 114 | + 'state2' => '', |
| 115 | + 'zip2' => '', |
| 116 | + 'country2' => '', |
| 117 | + 'size' => '', |
| 118 | + 'premium_language' => 'en', |
| 119 | + 'card_num' => '', |
| 120 | + 'card_type' => '', |
| 121 | + 'expiration' => '', |
| 122 | + 'cvv' => '', |
| 123 | + 'currency' => '', |
| 124 | + 'payment_method' => '', |
| 125 | + 'numAttempt' => '0', |
| 126 | + 'referrer' => '', |
| 127 | + 'utm_source' => '..cc', |
| 128 | + 'utm_medium' => '', |
| 129 | + 'utm_campaign' => '', |
| 130 | + 'language' => '', |
| 131 | + 'comment-option' => '', |
| 132 | + 'comment' => '', |
| 133 | + 'email-opt' => '', |
| 134 | + 'test_string' => '', |
| 135 | + '_cache_' => '', |
| 136 | + 'token' => '', |
| 137 | + 'contribution_tracking_id' => '', |
| 138 | + 'data_hash' => '', |
| 139 | + 'action' => '', |
| 140 | + 'gateway' => '', |
| 141 | + 'owa_session' => '', |
| 142 | + 'owa_ref' => '', |
| 143 | + ); |
| 144 | + unset($returned['order_id']); |
| 145 | + unset($returned['i_order_id']); |
| 146 | + $this->assertEquals($returned, $expected, "Staged post data does not match expected (largely empty)."); |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * |
| 151 | + */ |
| 152 | + public function testConstructAsTest(){ |
| 153 | + $ddObj = new DonationData('', true); //test mode from the start, no data |
| 154 | + $returned = $ddObj->getData(); |
| 155 | + $expected = array( |
| 156 | + 'amount' => '35', |
| 157 | + 'email' => 'test@example.com', |
| 158 | + 'fname' => 'Tester', |
| 159 | + 'mname' => 'T.', |
| 160 | + 'lname' => 'Testington', |
| 161 | + 'street' => '548 Market St.', |
| 162 | + 'city' => 'San Francisco', |
| 163 | + 'state' => 'CA', |
| 164 | + 'zip' => '94104', |
| 165 | + 'country' => 'US', |
| 166 | + 'fname2' => 'Testy', |
| 167 | + 'lname2' => 'Testerson', |
| 168 | + 'street2' => '123 Telegraph Ave.', |
| 169 | + 'city2' => 'Berkeley', |
| 170 | + 'state2' => 'CA', |
| 171 | + 'zip2' => '94703', |
| 172 | + 'country2' => 'US', |
| 173 | + 'size' => 'small', |
| 174 | + 'premium_language' => 'es', |
| 175 | + 'card_num' => '378282246310005', |
| 176 | + 'card_type' => 'american', |
| 177 | + 'expiration' => '1012', |
| 178 | + 'cvv' => '001', |
| 179 | + 'currency' => 'USD', |
| 180 | + 'payment_method' => '', |
| 181 | + 'i_order_id' => '1234567890', |
| 182 | + 'numAttempt' => '0', |
| 183 | + 'referrer' => 'http://www.baz.test.com/index.php?action=foo&action=bar', |
| 184 | + 'utm_source' => '..cc', |
| 185 | + 'utm_medium' => '', |
| 186 | + 'utm_campaign' => '', |
| 187 | + 'language' => 'en', |
| 188 | + 'comment-option' => '', |
| 189 | + 'comment' => '', |
| 190 | + 'email-opt' => '', |
| 191 | + 'test_string' => '', |
| 192 | + 'token' => '', |
| 193 | + 'contribution_tracking_id' => '', |
| 194 | + 'data_hash' => '', |
| 195 | + 'action' => '', |
| 196 | + 'gateway' => 'payflowpro', |
| 197 | + 'owa_session' => '', |
| 198 | + 'owa_ref' => 'http://localhost/defaultTestData', |
| 199 | + ); |
| 200 | + unset($returned['order_id']); |
| 201 | + |
| 202 | + $this->assertEquals($expected, $returned, "Staged default test data does not match expected."); |
| 203 | + } |
| 204 | + |
| 205 | + /** |
| 206 | + * |
| 207 | + */ |
| 208 | + public function testRepopulate(){ |
| 209 | + $expected = $this->testData; |
| 210 | + //just unset a handfull... doesn't matter what, really. |
| 211 | + unset($expected['comment-option']); |
| 212 | + unset($expected['email-opt']); |
| 213 | + unset($expected['test_string']); |
| 214 | + |
| 215 | + $ddObj = new DonationData(''); |
| 216 | + $ddObj->populateData(true, $expected); //change to test mode with explicit test data |
| 217 | + $returned = $ddObj->getData(); |
| 218 | + //unset these, because they're always new |
| 219 | + unset($returned['order_id']); |
| 220 | + unset($expected['order_id']); |
| 221 | + $this->assertEquals($returned, $expected, "The forced test data did not populate as expected."); |
| 222 | + } |
| 223 | + |
| 224 | + /** |
| 225 | + * |
| 226 | + */ |
| 227 | + public function testIsSomething(){ |
| 228 | + $data = $this->testData; |
| 229 | + unset($data['zip']); |
| 230 | + $ddObj = new DonationData('', true, $data); |
| 231 | + $this->assertEquals($ddObj->isSomething('zip'), false, "Zip should currently be nothing."); |
| 232 | + $this->assertEquals($ddObj->isSomething('lname'), true, "Lname should currently be something."); |
| 233 | + } |
| 234 | + |
| 235 | + /** |
| 236 | + * |
| 237 | + */ |
| 238 | + public function testGetVal(){ |
| 239 | + $data = $this->testData; |
| 240 | + unset($data['zip']); |
| 241 | + $ddObj = new DonationData('', true, $data); |
| 242 | + $this->assertEquals($ddObj->getVal('zip'), null, "Zip should currently be nothing."); |
| 243 | + $this->assertEquals($ddObj->getVal('lname'), 'McTestingyou', "Lname should currently be 'McTestingyou'."); |
| 244 | + } |
| 245 | + |
| 246 | + /** |
| 247 | + * |
| 248 | + */ |
| 249 | + public function testSetNormalizedAmount_amtGiven() { |
| 250 | + $data = $this->testData; |
| 251 | + $data['amount'] = 'this is not a number'; |
| 252 | + $data['amountGiven'] = 42.50; |
| 253 | + //unset($data['zip']); |
| 254 | + $ddObj = new DonationData('', true, $data); |
| 255 | + $returned = $ddObj->getData(); |
| 256 | + $this->assertEquals($returned['amount'], '42.50', "Amount was not properly reset"); |
| 257 | + $this->assertTrue(!(array_key_exists('amountGiven', $returned)), "amountGiven should have been removed from the data"); |
| 258 | + } |
| 259 | + |
| 260 | + /** |
| 261 | + * |
| 262 | + */ |
| 263 | + public function testSetNormalizedAmount_amount() { |
| 264 | + $data = $this->testData; |
| 265 | + $data['amount'] = 88.15; |
| 266 | + $data['amountGiven'] = 42.50; |
| 267 | + //unset($data['zip']); |
| 268 | + $ddObj = new DonationData('', true, $data); |
| 269 | + $returned = $ddObj->getData(); |
| 270 | + $this->assertEquals($returned['amount'], 88.15, "Amount was not properly reset"); |
| 271 | + $this->assertTrue(!(array_key_exists('amountGiven', $returned)), "amountGiven should have been removed from the data"); |
| 272 | + } |
| 273 | + |
| 274 | + /** |
| 275 | + * |
| 276 | + */ |
| 277 | + public function testSetNormalizedAmount_neagtiveAmount() { |
| 278 | + $data = $this->testData; |
| 279 | + $data['amount'] = -1; |
| 280 | + $data['amountOther'] = 3.25; |
| 281 | + //unset($data['zip']); |
| 282 | + $ddObj = new DonationData('', true, $data); |
| 283 | + $returned = $ddObj->getData(); |
| 284 | + $this->assertEquals($returned['amount'], 3.25, "Amount was not properly reset"); |
| 285 | + $this->assertTrue(!(array_key_exists('amountOther', $returned)), "amountOther should have been removed from the data"); |
| 286 | + } |
| 287 | + |
| 288 | + /** |
| 289 | + * |
| 290 | + */ |
| 291 | + public function testSetNormalizedAmount_noGoodAmount() { |
| 292 | + $data = $this->testData; |
| 293 | + $data['amount'] = 'splunge'; |
| 294 | + $data['amountGiven'] = 'wombat'; |
| 295 | + $data['amountOther'] = 'macedonia'; |
| 296 | + //unset($data['zip']); |
| 297 | + $ddObj = new DonationData('', true, $data); |
| 298 | + $returned = $ddObj->getData(); |
| 299 | + $this->assertEquals($returned['amount'], 0.00, "Amount was not properly reset"); |
| 300 | + $this->assertTrue(!(array_key_exists('amountOther', $returned)), "amountOther should have been removed from the data"); |
| 301 | + $this->assertTrue(!(array_key_exists('amountGiven', $returned)), "amountGiven should have been removed from the data"); |
| 302 | + } |
| 303 | + |
| 304 | + /* |
| 305 | + * TODO: Make sure ALL these functions in DonationData are tested, either directly or through a calling function. |
| 306 | + * I know that's more regression-ish, but I stand by it. :p |
| 307 | + function isCache(){ |
| 308 | + function setOwaRefId(){ |
| 309 | + function setNormalizedOrderIDs(){ |
| 310 | + function generateOrderId() { |
| 311 | + public function sanitizeInput( &$value, $key, $flags=ENT_COMPAT, $double_encode=false ) { |
| 312 | + function setGateway(){ |
| 313 | + function doCacheStuff(){ |
| 314 | + function getAnnoyingOrderIDLogLinePrefix(){ |
| 315 | + public function getEditToken( $salt = '' ) { |
| 316 | + public static function generateToken( $salt = '' ) { |
| 317 | + function matchEditToken( $val, $salt = '' ) { |
| 318 | + function unsetEditToken() { |
| 319 | + public static function ensureSession() { |
| 320 | + public function checkTokens() { |
| 321 | + function wasPosted(){ |
| 322 | + public static function getUtmSource( $utm_source = null, $utm_source_id = null ) { |
| 323 | + public function getOptOuts() { |
| 324 | + public function getCleanTrackingData( $clean_optouts = false ) { |
| 325 | + function saveContributionTracking() { |
| 326 | + public static function insertContributionTracking( $tracking_data ) { |
| 327 | + public function updateContributionTracking( $force = false ) { |
| 328 | + |
| 329 | + */ |
| 330 | +} |
| 331 | + |
| 332 | + |
Property changes on: trunk/extensions/DonationInterface/tests/DonationDataTestCase.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 333 | + native |
Property changes on: trunk/extensions/DonationInterface/tests/coverage |
___________________________________________________________________ |
Added: svn:ignore |
2 | 334 | + * |
Index: trunk/extensions/DonationInterface/tests/DonationInterfaceTestCase.php |
— | — | @@ -0,0 +1,37 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Wikimedia Foundation |
| 5 | + * |
| 6 | + * LICENSE |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * @since r98249 |
| 19 | + * @author Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> |
| 20 | + */ |
| 21 | + |
| 22 | +/** |
| 23 | + * @see TestHelper.php |
| 24 | + */ |
| 25 | +require_once dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'TestHelper.php'; |
| 26 | + |
| 27 | +/** |
| 28 | + * @group Fundraising |
| 29 | + * @group QueueHandling |
| 30 | + * @group ClassMethod |
| 31 | + * @group ListenerAdapter |
| 32 | + * |
| 33 | + * @category UnitTesting |
| 34 | + * @package Fundraising_QueueHandling |
| 35 | + */ |
| 36 | +abstract class DonationInterfaceTestCase extends PHPUnit_Framework_TestCase |
| 37 | +{ |
| 38 | +} |
Property changes on: trunk/extensions/DonationInterface/tests/DonationInterfaceTestCase.php |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 39 | + text/plain |
Added: svn:keywords |
2 | 40 | + Author Date HeadURL Header Id Revision |
Added: svn:eol-style |
3 | 41 | + native |
Property changes on: trunk/extensions/DonationInterface/tests |
___________________________________________________________________ |
Added: svn:ignore |
4 | 42 | + TestConfiguration.php |
Index: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect_resultswitcher.body.php |
— | — | @@ -0,0 +1,102 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class GlobalCollectGatewayResult extends GatewayForm { |
| 5 | + |
| 6 | + /** |
| 7 | + * Defines the action to take on a PFP transaction. |
| 8 | + * |
| 9 | + * Possible values include 'process', 'challenge', |
| 10 | + * 'review', 'reject'. These values can be set during |
| 11 | + * data processing validation, for instance. |
| 12 | + * |
| 13 | + * Hooks are exposed to handle the different actions. |
| 14 | + * |
| 15 | + * Defaults to 'process'. |
| 16 | + * @var string |
| 17 | + */ |
| 18 | + public $action = 'process'; |
| 19 | + |
| 20 | + /** |
| 21 | + * An array of form errors |
| 22 | + * @var array |
| 23 | + */ |
| 24 | + public $errors = array( ); |
| 25 | + |
| 26 | + /** |
| 27 | + * Constructor - set up the new special page |
| 28 | + */ |
| 29 | + public function __construct() { |
| 30 | + $this->adapter = new GlobalCollectAdapter(); |
| 31 | + parent::__construct(); //the next layer up will know who we are. |
| 32 | + } |
| 33 | + |
| 34 | + /** |
| 35 | + * Show the special page |
| 36 | + * |
| 37 | + * @param $par Mixed: parameter passed to the page or null |
| 38 | + */ |
| 39 | + public function execute( $par ) { |
| 40 | + global $wgRequest, $wgOut, $wgExtensionAssetsPath; |
| 41 | + |
| 42 | + $referrer = $wgRequest->getHeader( 'referer' ); |
| 43 | + |
| 44 | + global $wgServer; |
| 45 | + //TODO: Whitelist! We only want to do this for servers we are configured to like! |
| 46 | + //I didn't do this already, because this may turn out to be backwards anyway. It might be good to do the work in the iframe, |
| 47 | + //and then pop out. Maybe. We're probably going to have to test it a couple different ways, for user experience. |
| 48 | + //However, we're _definitely_ going to need to pop out _before_ we redirect to the thank you or fail pages. |
| 49 | + if ( strpos( $referrer, $wgServer ) === false ) { |
| 50 | + $wgOut->allowClickjacking(); |
| 51 | + $wgOut->addModules( 'iframe.liberator' ); |
| 52 | + return; |
| 53 | + } |
| 54 | + |
| 55 | + $wgOut->addExtensionStyle( |
| 56 | + $wgExtensionAssetsPath . '/DonationInterface/gateway_forms/css/gateway.css?284' . |
| 57 | + $this->adapter->getGlobal( 'CSSVersion' ) ); |
| 58 | + |
| 59 | + $this->setHeaders(); |
| 60 | + |
| 61 | + |
| 62 | + // dispatch forms/handling |
| 63 | + if ( $this->adapter->checkTokens() ) { |
| 64 | + // Display form for the first time |
| 65 | + $oid = $wgRequest->getText( 'order_id' ); |
| 66 | + $adapter_oid = $this->adapter->getData(); |
| 67 | + $adapter_oid = $adapter_oid['order_id']; |
| 68 | + if ( $oid && !empty( $oid ) && $oid === $adapter_oid ) { |
| 69 | + if ( !array_key_exists( 'order_status', $_SESSION ) || !array_key_exists( $oid, $_SESSION['order_status'] ) ) { |
| 70 | + $_SESSION['order_status'][$oid] = $this->adapter->do_transaction( 'GET_ORDERSTATUS' ); |
| 71 | + $_SESSION['order_status'][$oid]['data']['count'] = 0; |
| 72 | + } else { |
| 73 | + $_SESSION['order_status'][$oid]['data']['count'] = $_SESSION['order_status'][$oid]['data']['count'] + 1; |
| 74 | + } |
| 75 | + $result = $_SESSION['order_status'][$oid]; |
| 76 | + $this->displayResultsForDebug( $result ); |
| 77 | + //do the switching between the... stuff. |
| 78 | + |
| 79 | + switch ( $this->adapter->getTransactionWMFStatus() ) { |
| 80 | + case 'complete': |
| 81 | + case 'pending': |
| 82 | + case 'pending-poke': |
| 83 | + $go = $this->adapter->getThankYouPage(); |
| 84 | + break; |
| 85 | + case 'failed': |
| 86 | + $go = $this->adapter->getFailPage(); |
| 87 | + break; |
| 88 | + } |
| 89 | + |
| 90 | + $wgOut->addHTML( "<br>Redirecting to page $go" ); |
| 91 | + $wgOut->redirect( $go ); |
| 92 | + } |
| 93 | + $this->adapter->log( "Not posted, or not processed. Showing the form for the first time." ); |
| 94 | + } else { |
| 95 | + if ( !$this->adapter->isCache() ) { |
| 96 | + // if we're not caching, there's a token mismatch |
| 97 | + $this->errors['general']['token-mismatch'] = wfMsg( 'payflowpro_gateway-token-mismatch' ); |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +// end class |
Property changes on: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect_resultswitcher.body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 104 | + native |
Index: trunk/extensions/DonationInterface/globalcollect_gateway/forms/html/demo.html |
— | — | @@ -0,0 +1,162 @@ |
| 2 | +<table width="100%" cellspacing="0" cellpadding="0" border="0"><tr><td id="appeal" valign="top"><h2 id="appeal-head"> <span class="mw-headline" id="An_appeal_from_Wikipedia_founder_Jimmy_Wales">An appeal from Wikipedia founder Jimmy Wales</span></h2> |
| 3 | +<div class="plainlinks" id="appeal-body">I got a lot of funny looks ten years ago when I started talking to people about Wikipedia. |
| 4 | +<p>Let’s just say some people were skeptical of the notion that volunteers from all across the world could come together to create a remarkable pool of human knowledge – all for the simple purpose of sharing.</p> |
| 5 | +<p>No ads. No agenda. No strings attached.</p> |
| 6 | +<p>A decade after its founding, nearly 400 million people use Wikipedia and its sister sites every month - almost a third of the Internet-connected world.</p> |
| 7 | +<p>It is the 5th most popular website in the world but Wikipedia isn’t anything like a commercial website. It is a community creation, written by volunteers making one entry at a time. You are part of our community. And I’m writing today to ask you to protect and sustain Wikipedia.</p> |
| 8 | +<p>Together, we can keep it free of charge and free of advertising. We can keep it open – you can use the information in Wikipedia any way you want. We can keep it growing – spreading knowledge everywhere, and inviting participation from everyone.</p> |
| 9 | + |
| 10 | +<p>Each year at this time, we reach out to ask you and others all across the Wikimedia community to help sustain our joint enterprise with a modest donation of $20, $35, $50 or more.</p> |
| 11 | +<p>If you value Wikipedia as a source of information – and a source of inspiration – I hope you’ll choose to act right now.</p> |
| 12 | +<p>All the best,</p> |
| 13 | +<p><b>Jimmy Wales</b></p> |
| 14 | +<p>Founder, Wikipedia</p> |
| 15 | +<p>P.S. Wikipedia is about the power of people like us to do extraordinary things. People like us write Wikipedia, one word at a time. People like us fund it, one donation at a time. It's proof of our collective potential to change the world.</p> |
| 16 | +<p><br /> |
| 17 | +</p> |
| 18 | +</div> |
| 19 | +</td><td id="donate" valign="top"> |
| 20 | +<noscript><div id="noscript"><p id="noscript-msg">It appears that you do not have JavaScript enabled, or your browser does not support it. |
| 21 | +In order to provide a safe, secure and pleasant experience, our donation form requires JavaScript.</p><p id="noscript-redirect-msg">If you cannot or do not wish to enable JavaScript, you may still contribute by visiting:</p><p id="noscript-redirect-link"><a href="http://wikimediafoundation.org/wiki/DonateNonJS/en">http://wikimediafoundation.org/wiki/DonateNonJS/en</a></p></div></noscript> |
| 22 | + |
| 23 | +<h2 id="donate-head">Make your donation now</h2> |
| 24 | +<p class='creditcard-error-msg'>#general</p> |
| 25 | +<p class='creditcard-error-msg'>#retryMsg</p> |
| 26 | +<form name="payment" method="post" action="/index.php/Special:PayflowProGateway?form_name=RapidHtml&ffname=demo" autocomplete="off"> |
| 27 | + <div id="payflowpro_gateway-personal-info"><table id="payflow-table-donor"> |
| 28 | + <tr> |
| 29 | + <td colspan=2><span class="creditcard-error-msg">#fname</span></td> |
| 30 | + </tr> |
| 31 | + <tr> |
| 32 | + <td colspan=2><span class="creditcard-error-msg">#lname</span></td> |
| 33 | + </tr> |
| 34 | + <tr> |
| 35 | + <td class="label"><label for="fname">Name</label></td> |
| 36 | + <td> |
| 37 | + <input name="fname" size="30" value="@fname" type="text" onfocus="clearField( this, 'First' )" maxlength="25" class="required" id="fname" /> |
| 38 | + <input name="lname" size="30" value="@lname" type="text" onfocus="clearField( this, 'Last' )" maxlength="25" id="lname" /> |
| 39 | + </td> |
| 40 | + </tr> |
| 41 | + <tr> |
| 42 | + <td colspan=2><span class="creditcard-error-msg">#emailAdd</span></td> |
| 43 | + </tr> |
| 44 | + <tr> |
| 45 | + <td class="label"><label for="emailAdd">Email address</label></td> |
| 46 | + <td><input name="emailAdd" size="30" value="@emailAdd" type="text" maxlength="64" id="emailAdd" class="fullwidth" /></td> |
| 47 | + </tr> |
| 48 | + <tr> |
| 49 | + <td colspan="2"><span class="creditcard-error-msg">#amount</span></td> |
| 50 | + </tr> |
| 51 | + <tr> |
| 52 | + <td class="label"><label for="amount">Amount</label></td> |
| 53 | + <td> |
| 54 | + <input name="amount" size="7" value="@amount" type="text" maxlength="10" id="amount" /> |
| 55 | + <select name="currency_code" id="input_currency_code"> |
| 56 | + <option value="USD">USD: U.S. Dollar</option><option value="GBP">GBP: British Pound</option><option value="EUR">EUR: Euro</option><option value="AUD">AUD: Australian Dollar</option><option value="CAD">CAD: Canadian Dollar</option><option value="JPY">JPY: Japanese Yen</option> |
| 57 | + </select> |
| 58 | + </td> |
| 59 | + </tr> |
| 60 | + <tr> |
| 61 | + <td /> |
| 62 | + <td><img src="/extensions/DonationInterface/payflowpro_gateway/includes/credit_card_logos.gif" /></td> |
| 63 | + </tr> |
| 64 | + <tr> |
| 65 | + <td class="label"><label for="card_num">Card number</label></td> |
| 66 | + <td><input name="card_num" size="30" value="@card_num" type="text" maxlength="100" id="card_num" class="fullwidth" autocomplete="off" /></td> |
| 67 | + </tr> |
| 68 | + <tr> |
| 69 | + <td colspan=2><span class="creditcard-error-msg">#cvv</span></td> |
| 70 | + <tr> |
| 71 | + <td class="label"><label for="cvv">Security code</label></td> |
| 72 | + <td><input name="cvv" size="5" value="@cvv" type="text" maxlength="10" id="cvv" autocomplete="off" /> <a href="javascript:PopupCVV();">Where is this?</a></td> |
| 73 | + </tr> |
| 74 | + <tr> |
| 75 | + <td class="label"><label for="expiration">Expiration date</label></td> |
| 76 | + <td> |
| 77 | + <select name="mos" id="expiration"> |
| 78 | + <option value="01">1 (January)</option><option value="02">2 (February)</option><option value="03">3 (March)</option><option value="04">4 (April)</option><option value="05">5 (May)</option><option value="06">6 (June)</option><option value="07">7 (July)</option><option value="08">8 (August)</option><option value="09">9 (September)</option><option value="10">10 (October)</option><option value="11">11 (November)</option><option value="12">12 (December)</option> |
| 79 | + </select> |
| 80 | + <select name="year" id="year"> |
| 81 | + <option value="2010">2010</option><option value="2011">2011</option><option value="2012">2012</option><option value="2013">2013</option><option value="2014">2014</option><option value="2015">2015</option><option value="2016">2016</option><option value="2017">2017</option><option value="2018">2018</option><option value="2019">2019</option><option value="2020">2020</option> |
| 82 | + </select> |
| 83 | + </td> |
| 84 | + </tr> |
| 85 | + <tr> |
| 86 | + <td colspan=2><span class="creditcard-error-msg">#street</span></td> |
| 87 | + </tr> |
| 88 | + <tr> |
| 89 | + <td class="label"><label for="street">Street</label></td> |
| 90 | + <td><input name="street" size="30" value="@street" type="text" maxlength="100" id="street" class="fullwidth" /></td> |
| 91 | + </tr> |
| 92 | + <tr> |
| 93 | + <td colspan=2><span class="creditcard-error-msg">#city</span></td> |
| 94 | + </tr> |
| 95 | + <tr> |
| 96 | + <td class="label"><label for="city">City</label></td> |
| 97 | + <td><input name="city" size="30" value="@city" type="text" maxlength="40" id="city" class="fullwidth" /></td> |
| 98 | + </tr> |
| 99 | + <tr> |
| 100 | + <td colspan=2><span class="creditcard-error-msg">#state</span></td> |
| 101 | + </tr> |
| 102 | + <tr> |
| 103 | + <td class="label"><label for="state">State</label></td> |
| 104 | + <td> |
| 105 | + <select name="state" id="state"> |
| 106 | + <option value="YY">Select a State</option><option value="XX">Outside the U.S.</option><option value="AK">Alaska</option><option value="AL">Alabama</option><option value="AR">Arkansas</option><option value="AZ">Arizona</option><option value="CA">California</option><option value="CO">Colorado</option><option value="CT">Connecticut</option><option value="DC">Washington D.C.</option><option value="DE">Delaware</option><option value="FL">Florida</option><option value="GA">Georgia</option><option value="HI">Hawaii</option><option value="IA">Iowa</option><option value="ID">Idaho</option><option value="IL">Illinois</option><option value="IN">Indiana</option><option value="KS">Kansas</option><option value="KY">Kentucky</option><option value="LA">Louisiana</option><option value="MA">Massachusetts</option><option value="MD">Maryland</option><option value="ME">Maine</option><option value="MI">Michigan</option><option value="MN">Minnesota</option><option value="MO">Missouri</option><option value="MS">Mississippi</option><option value="MT">Montana</option><option value="NC">North Carolina</option><option value="ND">North Dakota</option><option value="NE">Nebraska</option><option value="NH">New Hampshire</option><option value="NJ">New Jersey</option><option value="NM">New Mexico</option><option value="NV">Nevada</option><option value="NY">New York</option><option value="OH">Ohio</option><option value="OK">Oklahoma</option><option value="OR">Oregon</option><option value="PA">Pennsylvania</option><option value="PR">Puerto Rico</option><option value="RI">Rhode Island</option><option value="SC">South Carolina</option><option value="SD">South Dakota</option><option value="TN">Tennessee</option><option value="TX">Texas</option><option value="UT">Utah</option><option value="VA">Virginia</option><option value="VT">Vermont</option><option value="WA">Washington</option><option value="WI">Wisconsin</option><option value="WV">West Virginia</option><option value="WY">Wyoming</option><option value="AA">AA</option><option value="AE">AE</option><option value="AP">AP</option> |
| 107 | + </select> |
| 108 | + </td> |
| 109 | + </tr> |
| 110 | + <tr> |
| 111 | + <td colspan=2><span class="creditcard-error-msg">#zip</span></td> |
| 112 | + </tr> |
| 113 | + <tr> |
| 114 | + <td class="label"><label for="zip">Postal code</label></td> |
| 115 | + <td><input name="zip" size="30" value="@zip" type="text" maxlength="9" id="zip" class="fullwidth" /></td> |
| 116 | + </tr> |
| 117 | + <tr> |
| 118 | + <td colspan=2><span class="creditcard-error-msg">#country</span></td> |
| 119 | + </tr> |
| 120 | + <tr> |
| 121 | + <td class="label"><label for="country">Country/Region</label></td> |
| 122 | + <td> |
| 123 | + <select name="country" id="country" onchange="return disableStates( this )"> |
| 124 | + <option value="004">Afghanistan</option><option value="008">Albania</option><option value="012">Algeria</option><option value="016">American Samoa</option><option value="020">Andorra</option><option value="024">Angola</option><option value="660">Anguilla</option><option value="010">Antarctica</option><option value="028">Antigua and Barbuda</option><option value="032">Argentina</option><option value="051">Armenia</option><option value="533">Aruba</option><option value="036">Australia</option><option value="040">Austria</option><option value="031">Azerbaijan</option><option value="044">Bahamas</option><option value="048">Bahrain</option><option value="050">Bangladesh</option><option value="052">Barbados</option><option value="112">Belarus</option><option value="056">Belgium</option><option value="084">Belize</option><option value="204">Benin</option><option value="060">Bermuda</option><option value="064">Bhutan</option><option value="068">Bolivia, Plurinational State of</option><option value="070">Bosnia and Herzegovina</option><option value="072">Botswana</option><option value="074">Bouvet Island</option><option value="076">Brazil</option><option value="086">British Indian Ocean Territory</option><option value="096">Brunei Darussalam</option><option value="100">Bulgaria</option><option value="854">Burkina Faso</option><option value="108">Burundi</option><option value="116">Cambodia</option><option value="120">Cameroon</option><option value="124">Canada</option><option value="132">Cape Verde</option><option value="136">Cayman Islands</option><option value="140">Central African Republic</option><option value="148">Chad</option><option value="152">Chile</option><option value="156">China</option><option value="162">Christmas Island</option><option value="166">Cocos (Keeling) Islands</option><option value="017">Colombia</option><option value="174">Comoros</option><option value="178">Congo</option><option value="180">Congo, the Democratic Republic of the</option><option value="184">Cook Islands</option><option value="188">Costa Rica</option><option value="384">Cote D'Ivoire</option><option value="191">Croatia</option><option value="192">Cuba</option><option value="196">Cyprus</option><option value="203">Czech Republic</option><option value="208">Denmark</option><option value="262">Djibouti</option><option value="212">Dominica</option><option value="214">Dominican Republic</option><option value="626">East Timor</option><option value="218">Ecuador</option><option value="818">Egypt</option><option value="222">El Salvador</option><option value="226">Equatorial Guinea</option><option value="232">Eritrea</option><option value="233">Estonia</option><option value="231">Ethiopia</option><option value="238">Falkland Islands (Malvinas)</option><option value="234">Faroe Islands</option><option value="242">Fiji</option><option value="246">Finland</option><option value="250">France</option><option value="254">French Guiana</option><option value="258">French Polynesia</option><option value="260">French Southern Territories</option><option value="266">Gabon</option><option value="270">Gambia</option><option value="268">Georgia</option><option value="276">Germany</option><option value="288">Ghana</option><option value="292">Gibraltar</option><option value="300">Greece</option><option value="304">Greenland</option><option value="308">Grenada</option><option value="312">Guadeloupe</option><option value="316">Guam</option><option value="320">Guatemala</option><option value="324">Guinea</option><option value="624">Guinea-Bissau</option><option value="328">Guyana</option><option value="332">Haiti</option><option value="334">Heard Island and McDonald Islands</option><option value="340">Honduras</option><option value="344">Hong Kong</option><option value="348">Hungary</option><option value="352">Iceland</option><option value="356">India</option><option value="360">Indonesia</option><option value="364">Iran, Islamic Republic of</option><option value="368">Iraq</option><option value="372">Ireland</option><option value="376">Israel</option><option value="380">Italy</option><option value="388">Jamaica</option><option value="392">Japan</option><option value="400">Jordan</option><option value="398">Kazakhstan</option><option value="404">Kenya</option><option value="296">Kiribati</option><option value="408">Korea, Democratic People's Republic of</option><option value="410">Korea, Republic of</option><option value="414">Kuwait</option><option value="417">Kyrgyzstan</option><option value="418">Laos</option><option value="428">Latvia</option><option value="422">Lebanon</option><option value="426">Lesotho</option><option value="430">Liberia</option><option value="434">Libyan Arab Jamahiriya</option><option value="438">Liechtenstein</option><option value="440">Lithuania</option><option value="442">Luxembourg</option><option value="446">Macao</option><option value="807">Macedonia</option><option value="450">Madagascar</option><option value="454">Malawi</option><option value="458">Malaysia</option><option value="462">Maldives</option><option value="466">Mali</option><option value="470">Malta</option><option value="584">Marshall Islands</option><option value="474">Martinique</option><option value="478">Mauritania</option><option value="480">Mauritius</option><option value="175">Mayotte</option><option value="484">Mexico</option><option value="583">Micronesia, Federated States of</option><option value="498">Moldova, Republic of</option><option value="492">Monaco</option><option value="496">Mongolia</option><option value="499">Montenegro</option><option value="500">Montserrat</option><option value="504">Morocco</option><option value="508">Mozambique</option><option value="104">Myanmar</option><option value="516">Namibia</option><option value="520">Nauru</option><option value="524">Nepal</option><option value="528">Netherlands</option><option value="530">Netherlands Antilles</option><option value="540">New Caledonia</option><option value="554">New Zealand</option><option value="558">Nicaragua</option><option value="562">Niger</option><option value="566">Nigeria</option><option value="570">Niue</option><option value="574">Norfolk Island</option><option value="580">Northern Mariana Islands</option><option value="578">Norway</option><option value="512">Oman</option><option value="586">Pakistan</option><option value="585">Palau</option><option value="591">Panama</option><option value="598">Papua New Guinea</option><option value="600">Paraguay</option><option value="604">Peru</option><option value="608">Philippines</option><option value="612">Pitcairn</option><option value="616">Poland</option><option value="620">Portugal</option><option value="630">Puerto Rico</option><option value="634">Qatar</option><option value="642">Romania</option><option value="643">Russian Federation</option><option value="646">Rwanda</option><option value="654">Saint Helena</option><option value="659">Saint Kitts and Nevis</option><option value="662">Saint Lucia</option><option value="666">Saint Pierre and Miquelon</option><option value="670">Saint Vincent and the Grenadines</option><option value="674">San Marino</option><option value="678">Sao Tome and Principe</option><option value="682">Saudi Arabia</option><option value="686">Senegal</option><option value="688">Serbia</option><option value="690">Seychelles</option><option value="694">Sierra Leone</option><option value="702">Singapore</option><option value="703">Slovakia</option><option value="705">Slovenia</option><option value="090">Solomon Islands</option><option value="706">Somalia</option><option value="710">South Africa</option><option value="724">Spain</option><option value="144">Sri Lanka</option><option value="736">Sudan</option><option value="740">Suriname</option><option value="744">Svalbard and Jan Mayen</option><option value="748">Swaziland</option><option value="752">Sweden</option><option value="756">Switzerland</option><option value="760">Syrian Arab Republic</option><option value="158">Taiwan</option><option value="762">Tajikistan</option><option value="834">Tanzania, United Republic of</option><option value="764">Thailand</option><option value="768">Togo</option><option value="772">Tokelau</option><option value="776">Tonga</option><option value="780">Trinidad and Tobago</option><option value="788">Tunisia</option><option value="792">Turkey</option><option value="795">Turkmenistan</option><option value="796">Turks and Caicos Islands</option><option value="798">Tuvalu</option><option value="800">Uganda</option><option value="804">Ukraine</option><option value="784">United Arab Emirates</option><option value="826">United Kingdom</option><option value="840">United States</option><option value="581">United States Minor Outlying Islands</option><option value="858">Uruguay</option><option value="860">Uzbekistan</option><option value="548">Vanuatu</option><option value="336">Vatican City State</option><option value="862">Venezuela, Bolivarian Republic of</option><option value="704">Viet Nam</option><option value="092">Virgin Islands, British</option><option value="850">Virgin Islands, U.S.</option><option value="876">Wallis and Futuna</option><option value="732">Western Sahara</option><option value="882">Western Samoa</option><option value="887">Yemen</option><option value="894">Zambia</option><option value="716">Zimbabwe</option> |
| 125 | + </select> |
| 126 | + </td> |
| 127 | + </tr> |
| 128 | + </table> |
| 129 | + </div> |
| 130 | + <!-- captcha --> |
| 131 | + @captcha |
| 132 | + <!-- end captcha --> |
| 133 | + <div id="payflowpro_gateway-form-submit"> |
| 134 | + <div id="mw-donate-submit-button"> |
| 135 | + <input class="button-plain" value="Donate by Credit Card" type="submit" /> |
| 136 | + </div> |
| 137 | + <div class="mw-donate-submessage" id="payflowpro_gateway-donate-submessage"> |
| 138 | + Your credit card will be securely processed. |
| 139 | + </div> |
| 140 | + </div> |
| 141 | + <input type="hidden" value="@utm_source" name="utm_source" /> |
| 142 | + <input type="hidden" value="@utm_medium" name="utm_medium" /> |
| 143 | + <input type="hidden" value="@utm_campaign" name="utm_campaign" /> |
| 144 | + <input type="hidden" value="@language" name="language" /> |
| 145 | + <input type="hidden" value="@referrer" name="referrer" /> |
| 146 | + <input type="hidden" value="@comment" name="comment" /> |
| 147 | + <input type="hidden" value="@comment-option" name="comment-option" /> |
| 148 | + <input type="hidden" value="@email-opt" name="email-opt" /> |
| 149 | + <input type="hidden" value="CreditCard" name="process" /> |
| 150 | + <input type="hidden" value="processed" name="payment_method" /> |
| 151 | + <input type="hidden" value="@token" name="token" /> |
| 152 | + <input type="hidden" value="@order_id" name="order_id" /> |
| 153 | + <input type="hidden" value="@numAttempt" name="numAttempt" /> |
| 154 | + <input type="hidden" value="@contribution_tracking_id" name="contribution_tracking_id" /> |
| 155 | + <input type="hidden" value="@data_hash" name="data_hash" /> |
| 156 | + <input type="hidden" value="@action" name="action" /> |
| 157 | + <input type="hidden" value="@owa_session" name="owa_session" /> |
| 158 | + <input type="hidden" value="@owa_ref" name="owa_ref" /> |
| 159 | +</form> |
| 160 | +<div class="payflow-cc-form-section" id="payflowpro_gateway-donate-addl-info"><div id="payflowpro_gateway-donate-addl-info-secure-logos"><p class=""><img src="/extensions/DonationInterface/payflowpro_gateway/includes/rapidssl_ssl_certificate-nonanimated.png"></p></div><div id="payflowpro_gateway-donate-addl-info-text"><p class=""><a href="http://wikimediafoundation.org/wiki/Ways_to_Give/en">Other ways to give</a></p><p class="">We do not store your credit card information, and your personal data is subject to our <a href="http://wikimediafoundation.org/wiki/Donor_Privacy_Policy">privacy policy</a>.</p><p class="">Questions or comments? Contact: <a href="mailto:donate@wikimedia.org">donate@wikimedia.org</a></p></div></div></td></tr></table><div class="printfooter"> |
| 161 | + |
| 162 | +Retrieved from "<a href="https://payments.wikimedia.org/index.php/Special:PayflowProGateway">https://payments.wikimedia.org/index.php/Special:PayflowProGateway</a>"</div> |
| 163 | + |
\ No newline at end of file |
Index: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect_gateway.body.php |
— | — | @@ -0,0 +1,232 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class GlobalCollectGateway extends GatewayForm { |
| 5 | + |
| 6 | + /** |
| 7 | + * Constructor - set up the new special page |
| 8 | + */ |
| 9 | + public function __construct() { |
| 10 | + $this->adapter = new GlobalCollectAdapter(); |
| 11 | + parent::__construct(); //the next layer up will know who we are. |
| 12 | + } |
| 13 | + |
| 14 | + /** |
| 15 | + * Show the special page |
| 16 | + * |
| 17 | + * @todo |
| 18 | + * - Add transaction type handler |
| 19 | + * - What should a failure on transaction_type issues do? log & message client |
| 20 | + * - Set up BANK_TRANSFER: Story #308 |
| 21 | + * |
| 22 | + * @param $par Mixed: parameter passed to the page or null |
| 23 | + */ |
| 24 | + public function execute( $par ) { |
| 25 | + global $wgRequest, $wgOut, $wgExtensionAssetsPath; |
| 26 | + $CSSVersion = $this->adapter->getGlobal( 'CSSVersion' ); |
| 27 | + |
| 28 | + $wgOut->allowClickjacking(); |
| 29 | + |
| 30 | + $wgOut->addExtensionStyle( |
| 31 | + $wgExtensionAssetsPath . '/DonationInterface/gateway_forms/css/gateway.css?284' . |
| 32 | + $CSSVersion ); |
| 33 | + |
| 34 | + // Hide unneeded interface elements |
| 35 | + $wgOut->addModules( 'donationInterface.skinOverride' ); |
| 36 | + |
| 37 | + $gateway_id = $this->adapter->getIdentifier(); |
| 38 | + |
| 39 | + $this->addErrorMessageScript(); |
| 40 | + |
| 41 | + // Make the wiki logo not clickable. |
| 42 | + // @fixme can this be moved into the form generators? |
| 43 | + $js = <<<EOT |
| 44 | +<script type="text/javascript"> |
| 45 | +jQuery(document).ready(function() { |
| 46 | + jQuery("div#p-logo a").attr("href","#"); |
| 47 | +}); |
| 48 | +</script> |
| 49 | +EOT; |
| 50 | + $wgOut->addHeadItem( 'logolinkoverride', $js ); |
| 51 | + |
| 52 | + $this->setHeaders(); |
| 53 | + |
| 54 | + /** |
| 55 | + * handle PayPal redirection |
| 56 | + * |
| 57 | + * if paypal redirection is enabled ($wgPayflowProGatewayPaypalURL must be defined) |
| 58 | + * and the PaypalRedirect form value must be true |
| 59 | + */ |
| 60 | + if ( $wgRequest->getText( 'PaypalRedirect', 0 ) ) { |
| 61 | + $this->paypalRedirect(); |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + //TODO: This is short-circuiting what I really want to do here. |
| 66 | + //so stop it. |
| 67 | + $data = $this->adapter->getDisplayData(); |
| 68 | + |
| 69 | + /* |
| 70 | + * The $transactionType should default to false. |
| 71 | + * |
| 72 | + * This is being introduced after INSERT_ORDERWITHPAYMENT was built. |
| 73 | + * Until all INSERT_ORDERWITHPAYMENT can be set in the proper forms, it |
| 74 | + * will be set as the default. |
| 75 | + */ |
| 76 | + $transactionType = false; |
| 77 | + $transactionType = ( isset( $data['transaction_type'] ) && !empty( $data['transaction_type'] ) ) ? $data['transaction_type'] : 'INSERT_ORDERWITHPAYMENT'; |
| 78 | + |
| 79 | + $this->adapter->log( '$transactionType: Default is set to: INSERT_ORDERWITHPAYMENT, this is a temporary hack for backwards compatibility.' ); |
| 80 | + $this->adapter->log( 'Setting transaction type: ' . ( string ) $data['transaction_type'] ); |
| 81 | + |
| 82 | + |
| 83 | + // dispatch forms/handling |
| 84 | + if ( $this->adapter->checkTokens() ) { |
| 85 | + if ( $this->adapter->posted && $data['payment_method'] == 'processed' ) { |
| 86 | + // The form was submitted and the payment method has been set |
| 87 | + $this->adapter->log( "Form posted and payment method set." ); |
| 88 | + |
| 89 | + // Check form for errors |
| 90 | + |
| 91 | + $options = array( ); |
| 92 | + switch ( $transactionType ) { |
| 93 | + |
| 94 | + case 'BANK_TRANSFER': |
| 95 | + $options['creditCard'] = false; |
| 96 | + break; |
| 97 | + |
| 98 | + case 'INSERT_ORDERWITHPAYMENT': |
| 99 | + $options['creditCard'] = false; |
| 100 | + break; |
| 101 | + |
| 102 | + default: |
| 103 | + $options['creditCard'] = false; |
| 104 | + } |
| 105 | + |
| 106 | + $form_errors = $this->validateForm( $data, $this->errors, $options ); |
| 107 | + unset( $options ); |
| 108 | + |
| 109 | + //$form_errors = $this->fnValidateForm( $data, $this->errors ); |
| 110 | + // If there were errors, redisplay form, otherwise proceed to next step |
| 111 | + if ( $form_errors ) { |
| 112 | + |
| 113 | + $this->displayForm( $data, $this->errors ); |
| 114 | + } else { // The submitted form data is valid, so process it |
| 115 | + // allow any external validators to have their way with the data |
| 116 | + // Execute the proper transaction code: |
| 117 | + switch ( $transactionType ) { |
| 118 | + |
| 119 | + case 'BANK_TRANSFER': |
| 120 | + $this->executeBankTransfer(); |
| 121 | + break; |
| 122 | + |
| 123 | + case 'INSERT_ORDERWITHPAYMENT': |
| 124 | + $this->executeInsertOrderWithPayment(); |
| 125 | + break; |
| 126 | + |
| 127 | + default: |
| 128 | + |
| 129 | + $message = 'The transaction type [ ' . $transactionType . ' ] was not found.'; |
| 130 | + throw new Exception( $message ); |
| 131 | + } |
| 132 | + |
| 133 | + |
| 134 | + //TODO: add all the hooks back in. |
| 135 | + } |
| 136 | + } else { |
| 137 | + // Display form for the first time |
| 138 | + $oid = $wgRequest->getText( 'order_id' ); |
| 139 | + if ( $oid && !empty( $oid ) ) { |
| 140 | + $wgOut->addHTML( "<pre>CAME BACK FROM SOMETHING.</pre>" ); |
| 141 | + $result = $this->adapter->do_transaction( 'GET_ORDERSTATUS' ); |
| 142 | + $this->displayResultsForDebug( $result ); |
| 143 | + } |
| 144 | + $this->adapter->log( "Not posted, or not processed. Showing the form for the first time." ); |
| 145 | + $this->displayForm( $data, $this->errors ); |
| 146 | + } |
| 147 | + } else { |
| 148 | + if ( !$this->adapter->isCache() ) { |
| 149 | + // if we're not caching, there's a token mismatch |
| 150 | + $this->errors['general']['token-mismatch'] = wfMsg( $gateway_id . '_gateway-token-mismatch' ); |
| 151 | + } |
| 152 | + $this->displayForm( $data, $this->errors ); |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + /** |
| 157 | + * Execute BANK_TRANSFER |
| 158 | + */ |
| 159 | + public function executeBankTransfer() { |
| 160 | + |
| 161 | + //global $wgOut; |
| 162 | + |
| 163 | + $result = $this->adapter->do_transaction( 'BANK_TRANSFER' ); |
| 164 | + $this->adapter->addDonorDataToSession(); |
| 165 | + |
| 166 | + $this->displayResultsForDebug( $result ); |
| 167 | + } |
| 168 | + |
| 169 | + /** |
| 170 | + * Execute INSERT_ORDERWITHPAYMENT |
| 171 | + */ |
| 172 | + public function executeInsertOrderWithPayment() { |
| 173 | + |
| 174 | + global $wgOut; |
| 175 | + |
| 176 | + $result = $this->adapter->do_transaction( 'INSERT_ORDERWITHPAYMENT' ); |
| 177 | + $this->adapter->addDonorDataToSession(); |
| 178 | + //$result = $this->adapter->do_transaction( 'TEST_CONNECTION' ); |
| 179 | + |
| 180 | + $this->displayResultsForDebug( $result ); |
| 181 | + |
| 182 | + if ( !empty( $result['data'] ) ) { |
| 183 | + |
| 184 | + if ( array_key_exists( 'FORMACTION', $result['data'] ) ) { |
| 185 | + $paymentFrame = Xml::openElement( 'iframe', array( |
| 186 | + 'id' => 'globalcollectframe', |
| 187 | + 'name' => 'globalcollectframe', |
| 188 | + 'width' => '680', |
| 189 | + 'height' => '300', |
| 190 | + 'frameborder' => '0', |
| 191 | + 'style' => 'display:block;', |
| 192 | + 'src' => $result['data']['FORMACTION'] |
| 193 | + ) |
| 194 | + ); |
| 195 | + $paymentFrame .= Xml::closeElement( 'iframe' ); |
| 196 | + |
| 197 | + $wgOut->addHTML( $paymentFrame ); |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + //TODO: Remember why the heck I decided to leave this here... |
| 203 | + //arguably, it's because it's slightly more "view" related, but... still, shouldn't you get stashed |
| 204 | + //in the new GatewayForm class so we can override in chlidren if we feel like it? Odd. |
| 205 | + function addErrorMessageScript() { |
| 206 | + global $wgOut; |
| 207 | + $gateway_id = $this->adapter->getIdentifier(); |
| 208 | + |
| 209 | + $scriptVars = array( |
| 210 | + $gateway_id . 'GatewayErrorMsgJs' => wfMsg( $gateway_id . '_gateway-error-msg-js' ), |
| 211 | + $gateway_id . 'GatewayErrorMsgEmail' => wfMsg( $gateway_id . '_gateway-error-msg-email' ), |
| 212 | + $gateway_id . 'GatewayErrorMsgAmount' => wfMsg( $gateway_id . '_gateway-error-msg-amount' ), |
| 213 | + $gateway_id . 'GatewayErrorMsgEmailAdd' => wfMsg( $gateway_id . '_gateway-error-msg-emailAdd' ), |
| 214 | + $gateway_id . 'GatewayErrorMsgFname' => wfMsg( $gateway_id . '_gateway-error-msg-fname' ), |
| 215 | + $gateway_id . 'GatewayErrorMsgLname' => wfMsg( $gateway_id . '_gateway-error-msg-lname' ), |
| 216 | + $gateway_id . 'GatewayErrorMsgStreet' => wfMsg( $gateway_id . '_gateway-error-msg-street' ), |
| 217 | + $gateway_id . 'GatewayErrorMsgCity' => wfMsg( $gateway_id . '_gateway-error-msg-city' ), |
| 218 | + $gateway_id . 'GatewayErrorMsgState' => wfMsg( $gateway_id . '_gateway-error-msg-state' ), |
| 219 | + $gateway_id . 'GatewayErrorMsgZip' => wfMsg( $gateway_id . '_gateway-error-msg-zip' ), |
| 220 | + $gateway_id . 'GatewayErrorMsgCountry' => wfMsg( $gateway_id . '_gateway-error-msg-country' ), |
| 221 | + $gateway_id . 'GatewayErrorMsgCardType' => wfMsg( $gateway_id . '_gateway-error-msg-card_type' ), |
| 222 | + $gateway_id . 'GatewayErrorMsgCardNum' => wfMsg( $gateway_id . '_gateway-error-msg-card_num' ), |
| 223 | + $gateway_id . 'GatewayErrorMsgExpiration' => wfMsg( $gateway_id . '_gateway-error-msg-expiration' ), |
| 224 | + $gateway_id . 'GatewayErrorMsgCvv' => wfMsg( $gateway_id . '_gateway-error-msg-cvv' ), |
| 225 | + $gateway_id . 'GatewayCVVExplain' => wfMsg( $gateway_id . '_gateway-cvv-explain' ), |
| 226 | + ); |
| 227 | + |
| 228 | + $wgOut->addScript( Skin::makeVariablesScript( $scriptVars ) ); |
| 229 | + } |
| 230 | + |
| 231 | +} |
| 232 | + |
| 233 | +// end class |
Property changes on: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect_gateway.body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 234 | + native |
Index: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect.adapter.php |
— | — | @@ -0,0 +1,367 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class GlobalCollectAdapter extends GatewayAdapter { |
| 5 | + const GATEWAY_NAME = 'Global Collect'; |
| 6 | + const IDENTIFIER = 'globalcollect'; |
| 7 | + const COMMUNICATION_TYPE = 'xml'; |
| 8 | + const GLOBAL_PREFIX = 'wgGlobalCollectGateway'; |
| 9 | + |
| 10 | + function defineAccountInfo() { |
| 11 | + $this->accountInfo = array( |
| 12 | + 'MERCHANTID' => self::getGlobal( 'MerchantID' ), |
| 13 | + //'IPADDRESS' => '', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm. |
| 14 | + 'VERSION' => "1.0", |
| 15 | + ); |
| 16 | + } |
| 17 | + |
| 18 | + function defineVarMap() { |
| 19 | + $this->var_map = array( |
| 20 | + 'ORDERID' => 'order_id', |
| 21 | + 'AMOUNT' => 'amount', |
| 22 | + 'CURRENCYCODE' => 'currency', |
| 23 | + 'LANGUAGECODE' => 'language', |
| 24 | + 'COUNTRYCODE' => 'country', |
| 25 | + 'MERCHANTREFERENCE' => 'order_id', |
| 26 | + 'RETURNURL' => 'returnto', //TODO: Fund out where the returnto URL is supposed to be coming from. |
| 27 | + 'IPADDRESS' => 'user_ip', //TODO: Not sure if this should be OUR ip, or the user's ip. Hurm. |
| 28 | + 'PAYMENTPRODUCTID' => 'card_type', |
| 29 | + 'CVV' => 'cvv', |
| 30 | + 'EXPIRYDATE' => 'expiration', |
| 31 | + 'CREDITCARDNUMBER' => 'card_num', |
| 32 | + 'FIRSTNAME' => 'fname', |
| 33 | + 'SURNAME' => 'lname', |
| 34 | + 'STREET' => 'street', |
| 35 | + 'CITY' => 'city', |
| 36 | + 'STATE' => 'state', |
| 37 | + 'ZIP' => 'zip', |
| 38 | + 'EMAIL' => 'email', |
| 39 | + ); |
| 40 | + } |
| 41 | + |
| 42 | + function defineReturnValueMap() { |
| 43 | + $this->return_value_map = array( |
| 44 | + 'OK' => true, |
| 45 | + 'NOK' => false, |
| 46 | + ); |
| 47 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'pending', 0, 70 ); |
| 48 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'failed', 100, 180 ); |
| 49 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'pending', 200 ); |
| 50 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'failed', 220, 280 ); |
| 51 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'pending', 300 ); |
| 52 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'failed', 310, 350 ); |
| 53 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'revised', 400 ); |
| 54 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'pending_poke', 525 ); |
| 55 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'pending', 550, 650 ); |
| 56 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'complete', 800, 975 ); //these are all post-authorized, but technically pre-settled... |
| 57 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'complete', 1000, 1050 ); |
| 58 | + $this->addCodeRange( 'GET_ORDERSTATUS', 'STATUSID', 'failed', 1100, 99999 ); |
| 59 | + } |
| 60 | + |
| 61 | + function defineTransactions() { |
| 62 | + $this->transactions = array( ); |
| 63 | + |
| 64 | + $this->transactions['BANK_TRANSFER'] = array( |
| 65 | + 'request' => array( |
| 66 | + 'REQUEST' => array( |
| 67 | + 'ACTION', |
| 68 | + 'META' => array( |
| 69 | + 'MERCHANTID', |
| 70 | + // 'IPADDRESS', |
| 71 | + 'VERSION' |
| 72 | + ), |
| 73 | + 'PARAMS' => array( |
| 74 | + 'ORDER' => array( |
| 75 | + 'ORDERID', |
| 76 | + 'AMOUNT', |
| 77 | + 'CURRENCYCODE', |
| 78 | + 'LANGUAGECODE', |
| 79 | + 'COUNTRYCODE', |
| 80 | + 'MERCHANTREFERENCE' |
| 81 | + ), |
| 82 | + 'PAYMENT' => array( |
| 83 | + 'PAYMENTPRODUCTID', |
| 84 | + 'AMOUNT', |
| 85 | + 'CURRENCYCODE', |
| 86 | + 'LANGUAGECODE', |
| 87 | + 'COUNTRYCODE', |
| 88 | + 'HOSTEDINDICATOR', |
| 89 | + 'RETURNURL', |
| 90 | +// 'INVOICENUMBER', |
| 91 | +// 'CUSTOMERBANKNAME', |
| 92 | +// 'CUSTOMERACCOUNTHOLDERNAME', |
| 93 | +// 'CUSTOMERBANKACCOUNT', |
| 94 | +// 'CUSTOMERBANKCITY', |
| 95 | + 'FIRSTNAME', |
| 96 | + 'SURNAME', |
| 97 | + 'STREET', |
| 98 | + 'CITY', |
| 99 | + 'STATE', |
| 100 | + 'ZIP', |
| 101 | + 'EMAIL', |
| 102 | + ) |
| 103 | + ) |
| 104 | + ) |
| 105 | + ), |
| 106 | + 'values' => array( |
| 107 | + 'ACTION' => 'INSERT_ORDERWITHPAYMENT', |
| 108 | + 'HOSTEDINDICATOR' => '1', |
| 109 | + 'PAYMENTPRODUCTID' => '11', |
| 110 | + ), |
| 111 | + ); |
| 112 | + |
| 113 | + $this->transactions['INSERT_ORDERWITHPAYMENT'] = array( |
| 114 | + 'request' => array( |
| 115 | + 'REQUEST' => array( |
| 116 | + 'ACTION', |
| 117 | + 'META' => array( |
| 118 | + 'MERCHANTID', |
| 119 | + // 'IPADDRESS', |
| 120 | + 'VERSION' |
| 121 | + ), |
| 122 | + 'PARAMS' => array( |
| 123 | + 'ORDER' => array( |
| 124 | + 'ORDERID', |
| 125 | + 'AMOUNT', |
| 126 | + 'CURRENCYCODE', |
| 127 | + 'LANGUAGECODE', |
| 128 | + 'COUNTRYCODE', |
| 129 | + 'MERCHANTREFERENCE' |
| 130 | + ), |
| 131 | + 'PAYMENT' => array( |
| 132 | + 'PAYMENTPRODUCTID', |
| 133 | + 'AMOUNT', |
| 134 | + 'CURRENCYCODE', |
| 135 | + 'LANGUAGECODE', |
| 136 | + 'COUNTRYCODE', |
| 137 | + 'HOSTEDINDICATOR', |
| 138 | + 'RETURNURL', |
| 139 | +// 'CVV', |
| 140 | +// 'EXPIRYDATE', |
| 141 | +// 'CREDITCARDNUMBER', |
| 142 | + 'FIRSTNAME', |
| 143 | + 'SURNAME', |
| 144 | + 'STREET', |
| 145 | + 'CITY', |
| 146 | + 'STATE', |
| 147 | + 'ZIP', |
| 148 | + 'EMAIL', |
| 149 | + ) |
| 150 | + ) |
| 151 | + ) |
| 152 | + ), |
| 153 | + 'values' => array( |
| 154 | + 'ACTION' => 'INSERT_ORDERWITHPAYMENT', |
| 155 | + 'HOSTEDINDICATOR' => '1', |
| 156 | + //'PAYMENTPRODUCTID' => '11', |
| 157 | + ), |
| 158 | + 'do_validation' => true, |
| 159 | + 'addDonorDataToSession' => true, |
| 160 | + ); |
| 161 | + |
| 162 | + $this->transactions['TEST_CONNECTION'] = array( |
| 163 | + 'request' => array( |
| 164 | + 'REQUEST' => array( |
| 165 | + 'ACTION', |
| 166 | + 'META' => array( |
| 167 | + 'MERCHANTID', |
| 168 | +// 'IPADDRESS', |
| 169 | + 'VERSION' |
| 170 | + ), |
| 171 | + 'PARAMS' => array( ) |
| 172 | + ) |
| 173 | + ), |
| 174 | + 'values' => array( |
| 175 | + 'ACTION' => 'TEST_CONNECTION' |
| 176 | + ) |
| 177 | + ); |
| 178 | + |
| 179 | + $this->transactions['GET_ORDERSTATUS'] = array( |
| 180 | + 'request' => array( |
| 181 | + 'REQUEST' => array( |
| 182 | + 'ACTION', |
| 183 | + 'META' => array( |
| 184 | + 'MERCHANTID', |
| 185 | +// 'IPADDRESS', |
| 186 | + 'VERSION' |
| 187 | + ), |
| 188 | + 'PARAMS' => array( |
| 189 | + 'ORDER' => array( |
| 190 | + 'ORDERID', |
| 191 | + ), |
| 192 | + ) |
| 193 | + ) |
| 194 | + ), |
| 195 | + 'values' => array( |
| 196 | + 'ACTION' => 'GET_ORDERSTATUS', |
| 197 | + 'VERSION' => '2.0' |
| 198 | + ), |
| 199 | + 'do_processhooks' => true, |
| 200 | + 'pullDonorDataFromSession' => true, |
| 201 | + 'loop_for_status' => array( |
| 202 | + //'pending', |
| 203 | + 'pending_poke', |
| 204 | + 'complete', |
| 205 | + 'failed', |
| 206 | + 'revised', |
| 207 | + ) |
| 208 | + ); |
| 209 | + } |
| 210 | + |
| 211 | + /** |
| 212 | + * Take the entire response string, and strip everything we don't care about. |
| 213 | + * For instance: If it's XML, we only want correctly-formatted XML. Headers must be killed off. |
| 214 | + * return a string. |
| 215 | + */ |
| 216 | + function getFormattedResponse( $rawResponse ) { |
| 217 | + $xmlString = $this->stripXMLResponseHeaders( $rawResponse ); |
| 218 | + $displayXML = $this->formatXmlString( $xmlString ); |
| 219 | + $realXML = new DomDocument( '1.0' ); |
| 220 | + self::log( "Here is the Raw XML: " . $displayXML ); //I am apparently a huge fibber. |
| 221 | + $realXML->loadXML( trim( $xmlString ) ); |
| 222 | + return $realXML; |
| 223 | + } |
| 224 | + |
| 225 | + /** |
| 226 | + * Parse the response to get the status. Not sure if this should return a bool, or something more... telling. |
| 227 | + */ |
| 228 | + function getResponseStatus( $response ) { |
| 229 | + |
| 230 | + $aok = true; |
| 231 | + |
| 232 | + foreach ( $response->getElementsByTagName( 'RESULT' ) as $node ) { |
| 233 | + if ( array_key_exists( $node->nodeValue, $this->return_value_map ) && $this->return_value_map[$node->nodeValue] !== true ) { |
| 234 | + $aok = false; |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | + return $aok; |
| 239 | + } |
| 240 | + |
| 241 | + /** |
| 242 | + * Parse the response to get the errors in a format we can log and otherwise deal with. |
| 243 | + * return a key/value array of codes (if they exist) and messages. |
| 244 | + */ |
| 245 | + function getResponseErrors( $response ) { |
| 246 | + $errors = array( ); |
| 247 | + foreach ( $response->getElementsByTagName( 'ERROR' ) as $node ) { |
| 248 | + $code = ''; |
| 249 | + $message = ''; |
| 250 | + foreach ( $node->childNodes as $childnode ) { |
| 251 | + if ( $childnode->nodeName === "CODE" ) { |
| 252 | + $code = $childnode->nodeValue; |
| 253 | + } |
| 254 | + if ( $childnode->nodeName === "MESSAGE" ) { |
| 255 | + $message = $childnode->nodeValue; |
| 256 | + } |
| 257 | + } |
| 258 | + $errors[$code] = $message; |
| 259 | + } |
| 260 | + return $errors; |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * Harvest the data we need back from the gateway. |
| 265 | + * return a key/value array |
| 266 | + */ |
| 267 | + function getResponseData( $response ) { |
| 268 | + $data = array( ); |
| 269 | + |
| 270 | + $transaction = $this->currentTransaction(); |
| 271 | + |
| 272 | + switch ( $transaction ) { |
| 273 | + case 'BANK_TRANSFER': |
| 274 | + $data = $this->xmlChildrenToArray( $response, 'ROW' ); |
| 275 | + $data['ORDER'] = $this->xmlChildrenToArray( $response, 'ORDER' ); |
| 276 | + $data['PAYMENT'] = $this->xmlChildrenToArray( $response, 'PAYMENT' ); |
| 277 | + break; |
| 278 | + case 'INSERT_ORDERWITHPAYMENT': |
| 279 | + $data = $this->xmlChildrenToArray( $response, 'ROW' ); |
| 280 | + $data['ORDER'] = $this->xmlChildrenToArray( $response, 'ORDER' ); |
| 281 | + $data['PAYMENT'] = $this->xmlChildrenToArray( $response, 'PAYMENT' ); |
| 282 | + break; |
| 283 | + case 'GET_ORDERSTATUS': |
| 284 | + $data = $this->xmlChildrenToArray( $response, 'STATUS' ); |
| 285 | + $this->setTransactionWMFStatus( $this->findCodeAction( 'GET_ORDERSTATUS', 'STATUSID', $data['STATUSID'] ) ); |
| 286 | + $data['ORDER'] = $this->xmlChildrenToArray( $response, 'ORDER' ); |
| 287 | + break; |
| 288 | + } |
| 289 | + |
| 290 | + |
| 291 | + self::log( "Returned Data: " . print_r( $data, true ) ); |
| 292 | + return $data; |
| 293 | + } |
| 294 | + |
| 295 | + function processResponse( $response ) { |
| 296 | + //set the transaction result message |
| 297 | + $responseStatus = isset( $response['STATUSID'] ) ? $response['STATUSID'] : ''; |
| 298 | + $this->setTransactionResult( "Response Status: " . $responseStatus, 'txn_message' ); //TODO: Translate for GC. |
| 299 | + $this->setTransactionResult( $this->getData( 'order_id' ), 'gateway_txn_id' ); |
| 300 | + } |
| 301 | + |
| 302 | + /** |
| 303 | + * The default section of the switch will be hit on first time forms. This |
| 304 | + * should be okay, because we are only concerned with staged_vars that have |
| 305 | + * been posted. |
| 306 | + * |
| 307 | + * Credit cards staged_vars are set to ensure form failures on validation in |
| 308 | + * the default case. This should prevent accidental form submission with |
| 309 | + * unknown transaction types. |
| 310 | + */ |
| 311 | + function defineStagedVars() { |
| 312 | + |
| 313 | + //OUR field names. |
| 314 | + $this->staged_vars = array( |
| 315 | + 'amount', |
| 316 | + 'card_type', |
| 317 | + //'card_num', |
| 318 | + 'returnto', |
| 319 | + 'order_id', //This may or may not oughta-be-here... |
| 320 | + ); |
| 321 | + } |
| 322 | + |
| 323 | + protected function stage_amount( $type = 'request' ) { |
| 324 | + switch ( $type ) { |
| 325 | + case 'request': |
| 326 | + $this->postdata['amount'] = $this->postdata['amount'] * 100; |
| 327 | + break; |
| 328 | + case 'response': |
| 329 | + $this->postdata['amount'] = $this->postdata['amount'] / 100; |
| 330 | + break; |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + protected function stage_card_type( $type = 'request' ) { |
| 335 | + |
| 336 | + $types = array( |
| 337 | + 'visa' => '1', |
| 338 | + 'mastercard' => '3', |
| 339 | + 'american' => '2', |
| 340 | + 'discover' => '128' |
| 341 | + ); |
| 342 | + |
| 343 | + if ( $type === 'response' ) { |
| 344 | + $types = array_flip( $types ); |
| 345 | + } |
| 346 | + |
| 347 | + if ( ( array_key_exists( 'card_type', $this->postdata ) ) && array_key_exists( $this->postdata['card_type'], $types ) ) { |
| 348 | + $this->postdata['card_type'] = $types[$this->postdata['card_type']]; |
| 349 | + } else { |
| 350 | + //$this->postdata['card_type'] = ''; |
| 351 | + //iono: maybe nothing? |
| 352 | + } |
| 353 | + } |
| 354 | + |
| 355 | + protected function stage_card_num( $type = 'request' ) { |
| 356 | + //I realize that the $type isn't used. Voodoo. |
| 357 | + if ( array_key_exists( 'card_num', $this->postdata ) ) { |
| 358 | + $this->postdata['card_num'] = str_replace( ' ', '', $this->postdata['card_num'] ); |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + protected function stage_returnto( $type = 'request' ) { |
| 363 | + if ( $type === 'request' ) { |
| 364 | + $this->postdata['returnto'] = $this->postdata['returnto'] . "?order_id=" . $this->postdata['order_id']; |
| 365 | + } |
| 366 | + } |
| 367 | + |
| 368 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/DonationInterface/globalcollect_gateway/globalcollect.adapter.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 369 | + native |
Index: trunk/extensions/DonationInterface/gateway_common/gateway.adapter.php |
— | — | @@ -0,0 +1,1266 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Wikimedia Foundation |
| 6 | + * |
| 7 | + * LICENSE |
| 8 | + * |
| 9 | + * This program is free software; you can redistribute it and/or modify |
| 10 | + * it under the terms of the GNU General Public License as published by |
| 11 | + * the Free Software Foundation; either version 2 of the License, or |
| 12 | + * (at your option) any later version. |
| 13 | + * |
| 14 | + * This program is distributed in the hope that it will be useful, |
| 15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | + * GNU General Public License for more details. |
| 18 | + * |
| 19 | + */ |
| 20 | + |
| 21 | +/** |
| 22 | + * GatewayType Interface |
| 23 | + * |
| 24 | + */ |
| 25 | +interface GatewayType { |
| 26 | + //all the particulars of the child classes. Aaaaall. |
| 27 | + |
| 28 | + /** |
| 29 | + * Take the entire response string, and strip everything we don't care about. |
| 30 | + * For instance: If it's XML, we only want correctly-formatted XML. Headers must be killed off. |
| 31 | + * return a string. |
| 32 | + */ |
| 33 | + function getFormattedResponse( $rawResponse ); |
| 34 | + |
| 35 | + /** |
| 36 | + * Parse the response to get the status. Not sure if this should return a bool, or something more... telling. |
| 37 | + */ |
| 38 | + function getResponseStatus( $response ); |
| 39 | + |
| 40 | + /** |
| 41 | + * Parse the response to get the errors in a format we can log and otherwise deal with. |
| 42 | + * return a key/value array of codes (if they exist) and messages. |
| 43 | + */ |
| 44 | + function getResponseErrors( $response ); |
| 45 | + |
| 46 | + /** |
| 47 | + * Harvest the data we need back from the gateway. |
| 48 | + * return a key/value array |
| 49 | + */ |
| 50 | + function getResponseData( $response ); |
| 51 | + |
| 52 | + /** |
| 53 | + * Actually do... stuff. Here. |
| 54 | + * TODO: Better comment. |
| 55 | + * Process the entire response gott'd by the last four functions. |
| 56 | + */ |
| 57 | + function processResponse( $response ); |
| 58 | + |
| 59 | + /** |
| 60 | + * Should be a list of our variables that need special staging. |
| 61 | + * @see $this->staged_vars |
| 62 | + */ |
| 63 | + function defineStagedVars(); |
| 64 | + |
| 65 | + /** |
| 66 | + * defineTransactions will define the $transactions array. |
| 67 | + * The array will contain everything we need to know about the request structure for all the transactions we care about, |
| 68 | + * for the current gateway. |
| 69 | + * First array key: Some way for us to id the transaction. Doesn't actually have to be the gateway's name for it, but I'm going with that until I have a reason not to. |
| 70 | + * Second array key: |
| 71 | + * 'request' contains the structure of that request. Leaves in the array tree will eventually be mapped to actual values of ours, |
| 72 | + * according to the precidence established in the getValue function. |
| 73 | + * 'values' contains default values for the transaction. Things that are typically not overridden should go here. |
| 74 | + */ |
| 75 | + function defineTransactions(); |
| 76 | + |
| 77 | + /** |
| 78 | + * defineVarMap needs to set up the $var_map array. |
| 79 | + * Keys = the name (or node name) value in the gateway transaction |
| 80 | + * Values = the mediawiki field name for the corresponding piece of data. |
| 81 | + */ |
| 82 | + function defineVarMap(); |
| 83 | + |
| 84 | + /** |
| 85 | + * defineAccountInfo needs to set up the $accountInfo array. |
| 86 | + * Keys = the name (or node name) value in the gateway transaction |
| 87 | + * Values = The actual values for those keys. Probably have to access a global or two. (use getGlobal()!) |
| 88 | + */ |
| 89 | + function defineAccountInfo(); |
| 90 | + |
| 91 | + /** |
| 92 | + * defineReturnValueMap sets up the $return_value_map array. |
| 93 | + * Keys = The different constants that may be contained as values in the gateway's response. |
| 94 | + * Values = what that string constant means to mediawiki. |
| 95 | + */ |
| 96 | + function defineReturnValueMap(); |
| 97 | +} |
| 98 | + |
| 99 | +/** |
| 100 | + * GatewayAdapter |
| 101 | + * |
| 102 | + */ |
| 103 | +abstract class GatewayAdapter implements GatewayType { |
| 104 | + |
| 105 | + //Contains the map of THEIR var names, to OURS. |
| 106 | + //I'd have gone the other way, but we'd run into 1:many pretty quick. |
| 107 | + protected $var_map; |
| 108 | + protected $accountInfo; |
| 109 | + protected $url; |
| 110 | + protected $transactions; |
| 111 | + |
| 112 | + /** |
| 113 | + * $transaction_type will be set in the GatewayForm::execute() |
| 114 | + * |
| 115 | + * @var string|false |
| 116 | + * |
| 117 | + * @see GatewayForm::execute() |
| 118 | + */ |
| 119 | + protected $transaction_type = false; |
| 120 | + |
| 121 | + /** |
| 122 | + * Staged variables. This is affected by the transaction type. |
| 123 | + * |
| 124 | + * @var array $staged_vars |
| 125 | + */ |
| 126 | + protected $staged_vars = array( ); |
| 127 | + protected $return_value_map; |
| 128 | + protected $postdata; |
| 129 | + protected $postdatadefaults; |
| 130 | + protected $xmlDoc; |
| 131 | + protected $dataObj; |
| 132 | + protected $transaction_results; |
| 133 | + protected $form_class; |
| 134 | + protected $validation_errors; |
| 135 | + public $action; //Currently, hooks need to be able to set this directly. |
| 136 | + public $debugarray; //TODO: Take me out. |
| 137 | + |
| 138 | + //ALL OF THESE need to be redefined in the children. Much voodoo depends on the accuracy of these constants. |
| 139 | + const GATEWAY_NAME = 'Donation Gateway'; |
| 140 | + const IDENTIFIER = 'donation'; |
| 141 | + const COMMUNICATION_TYPE = 'xml'; //this needs to be either 'xml' or 'namevalue' |
| 142 | + const GLOBAL_PREFIX = 'wgDonationGateway'; //...for example. |
| 143 | + |
| 144 | + /** |
| 145 | + * Constructor |
| 146 | + * |
| 147 | + * @param array $options |
| 148 | + * OPTIONAL - You may set options for testing |
| 149 | + * - testData - Submit test data |
| 150 | + * |
| 151 | + * @see DonationData |
| 152 | + */ |
| 153 | + public function __construct( $options = array( ) ) { |
| 154 | + global $wgRequest; |
| 155 | + |
| 156 | + // Extract the options |
| 157 | + extract( $options ); |
| 158 | + |
| 159 | + $testData = isset( $testData ) ? $testData : false; |
| 160 | + $postDefaults = isset( $postDefaults ) ? $postDefaults : false; |
| 161 | + |
| 162 | + if ( !self::getGlobal( 'Test' ) ) { |
| 163 | + $this->url = self::getGlobal( 'URL' ); |
| 164 | + |
| 165 | + // Only submit test data if we are in test mode. |
| 166 | + $testData = false; |
| 167 | + } else { |
| 168 | + $this->url = self::getGlobal( 'TestingURL' ); |
| 169 | + } |
| 170 | + |
| 171 | + $this->dataObj = new DonationData( get_called_class(), self::getGlobal( 'Test' ), $testData ); |
| 172 | + |
| 173 | + $this->postdata = $this->dataObj->getData(); |
| 174 | + //TODO: Fix this a bit. |
| 175 | + |
| 176 | + $this->posted = $wgRequest->wasPosted(); |
| 177 | + |
| 178 | + $this->setPostDefaults( $postDefaults ); |
| 179 | + $this->defineTransactions(); |
| 180 | + $this->defineVarMap(); |
| 181 | + $this->defineAccountInfo(); |
| 182 | + $this->defineReturnValueMap(); |
| 183 | + |
| 184 | + //Don't bother setting the transaction type if it's not something. |
| 185 | + if ( $this->dataObj->isSomething( 'transaction_type' ) ) { |
| 186 | + $this->currentTransaction( $this->postdata['transaction_type'] ); |
| 187 | + } |
| 188 | + |
| 189 | + $this->displaydata = $this->postdata; |
| 190 | + $this->stageData(); |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Override this in children if you want different defaults. |
| 195 | + */ |
| 196 | + function setPostDefaults( $options = array( ) ) { |
| 197 | + |
| 198 | + // Extract the options |
| 199 | + if ( is_array( $options ) ) { |
| 200 | + extract( $options ); |
| 201 | + } |
| 202 | + |
| 203 | + $returnTitle = isset( $returnTitle ) ? $returnTitle : Title::newFromText( 'Special:GlobalCollectGatewayResult' ); |
| 204 | + $returnTo = isset( $returnTo ) ? $returnTo : $returnTitle->getFullURL(); |
| 205 | + |
| 206 | + $this->postdatadefaults = array( |
| 207 | + 'order_id' => '112358' . rand(), |
| 208 | + 'amount' => '11.38', |
| 209 | + 'currency' => 'USD', |
| 210 | + 'language' => 'en', |
| 211 | + 'country' => 'US', |
| 212 | + 'returnto' => $returnTo, |
| 213 | + 'user_ip' => ( self::getGlobal( 'Test' ) ) ? '12.12.12.12' : wfGetIP(), // current user's IP address |
| 214 | + 'card_type' => 'visa', |
| 215 | + ); |
| 216 | + } |
| 217 | + |
| 218 | + function getThankYouPage() { |
| 219 | + global $wgLang; |
| 220 | + $language = $wgLang->getCode(); |
| 221 | + $page = self::getGlobal( "ThankYouPage" ) . "/$language"; |
| 222 | +// $returnTitle = Title::newFromText( $page ); |
| 223 | +// $returnto = $returnTitle->getFullURL(); |
| 224 | + return $page; |
| 225 | + } |
| 226 | + |
| 227 | + function getFailPage() { |
| 228 | + global $wgLang; |
| 229 | + $language = $wgLang->getCode(); |
| 230 | + $page = self::getGlobal( "FailPage" ) . "/$language"; |
| 231 | + $returnTitle = Title::newFromText( $page ); |
| 232 | + $returnto = $returnTitle->getFullURL(); |
| 233 | + return $returnto; |
| 234 | + } |
| 235 | + |
| 236 | + function checkTokens() { |
| 237 | + return $this->dataObj->checkTokens(); |
| 238 | + } |
| 239 | + |
| 240 | + function getData( $val = '' ) { |
| 241 | + if ( empty( $val ) ) { |
| 242 | + return $this->postdata; |
| 243 | + } else { |
| 244 | + if ( array_key_exists( $val, $this->postdata ) ) { |
| 245 | + return $this->postdata[$val]; |
| 246 | + } else { |
| 247 | + return null; |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + function getDisplayData() { |
| 253 | + return $this->displaydata; |
| 254 | + } |
| 255 | + |
| 256 | + function isCache() { |
| 257 | + return $this->dataObj->isCache(); |
| 258 | + } |
| 259 | + |
| 260 | + /** |
| 261 | + * This function is important. |
| 262 | + * All the globals in Donation Interface should be accessed in this manner |
| 263 | + * if they are meant to have a default value, but can be overridden by any |
| 264 | + * of the gateways. It will check to see if a gateway-specific global |
| 265 | + * exists, and if one is not set, it will pull the default from the |
| 266 | + * wgDonationInterface definitions. Through this function, it is no longer |
| 267 | + * necessary to define gateway-specific globals in LocalSettings unless you |
| 268 | + * wish to override the default value for all gateways. |
| 269 | + * @staticvar array $gotten A cache of all the globals we've already... |
| 270 | + * gotten. |
| 271 | + * @param type $varname The global value we're looking for. It will first |
| 272 | + * look for a global named for the instantiated gateway's GLOBAL_PREFIX, |
| 273 | + * plus the $varname value. If that doesn't come up with anything that has |
| 274 | + * been set, it will use the default value for all of donation interface, |
| 275 | + * stored in $wgDonationInterface . $varname. |
| 276 | + * @return mixed The configured value for that gateway if it exists. If not, |
| 277 | + * the configured value for Donation Interface if it exists or not. |
| 278 | + */ |
| 279 | + static function getGlobal( $varname ) { |
| 280 | + static $gotten = array( ); //cache. |
| 281 | + if ( !array_key_exists( $varname, $gotten ) ) { |
| 282 | + $globalname = self::getGlobalPrefix() . $varname; |
| 283 | + global $$globalname; |
| 284 | + if ( !isset( $$globalname ) ) { |
| 285 | + $globalname = "wgDonationInterface" . $varname; |
| 286 | + global $$globalname; //set or not. This is fine. |
| 287 | + } |
| 288 | + $gotten[$varname] = $$globalname; |
| 289 | + } |
| 290 | + return $gotten[$varname]; |
| 291 | + } |
| 292 | + |
| 293 | + function getValue( $gateway_field_name, $token = false ) { |
| 294 | + if ( empty( $this->transactions ) ) { |
| 295 | + //TODO: These dies should all throw exceptions or something less completely fatal. |
| 296 | + $msg = self::getGatewayName() . ': Transactions structure is empty! No transaction can be constructed.'; |
| 297 | + self::log( $msg, LOG_CRIT ); |
| 298 | + throw new MWException( $msg ); |
| 299 | + } |
| 300 | + //How do we determine the value of a field asked for in a particular transaction? |
| 301 | + $transaction = $this->currentTransaction(); |
| 302 | + |
| 303 | + //If there's a hard-coded value in the transaction definition, use that. |
| 304 | + if ( !empty( $transaction ) ) { |
| 305 | + if ( array_key_exists( $transaction, $this->transactions ) && is_array( $this->transactions[$transaction] ) && |
| 306 | + array_key_exists( 'values', $this->transactions[$transaction] ) && |
| 307 | + array_key_exists( $gateway_field_name, $this->transactions[$transaction]['values'] ) ) { |
| 308 | + return $this->transactions[$transaction]['values'][$gateway_field_name]; |
| 309 | + } |
| 310 | + } |
| 311 | + |
| 312 | + //if it's account info, use that. |
| 313 | + //$this->accountInfo; |
| 314 | + if ( array_key_exists( $gateway_field_name, $this->accountInfo ) ) { |
| 315 | + return $this->accountInfo[$gateway_field_name]; |
| 316 | + } |
| 317 | + |
| 318 | + |
| 319 | + //If there's a value in the post data (name-translated by the var_map), use that. |
| 320 | + if ( array_key_exists( $gateway_field_name, $this->var_map ) ) { |
| 321 | + if ( $token === true ) { //we just want the field name to use, so short-circuit all that mess. |
| 322 | + return '@' . $this->var_map[$gateway_field_name]; |
| 323 | + } |
| 324 | + if ( array_key_exists( $this->var_map[$gateway_field_name], $this->postdata ) && |
| 325 | + $this->postdata[$this->var_map[$gateway_field_name]] !== '' ) { |
| 326 | + //if it was sent, use that. |
| 327 | + return $this->postdata[$this->var_map[$gateway_field_name]]; |
| 328 | + } else { |
| 329 | + //return the default for that form value |
| 330 | + |
| 331 | + $tempField = isset( $this->var_map[ $gateway_field_name ] ) ? $this->var_map[ $gateway_field_name ] : false; |
| 332 | + |
| 333 | + $tempValue = ''; |
| 334 | + |
| 335 | + if ( $tempField && isset( $this->postdatadefaults[ $tempField ] ) ) { |
| 336 | + $tempValue = $this->postdatadefaults[ $tempField ]; |
| 337 | + } |
| 338 | + |
| 339 | + return $tempValue; |
| 340 | + } |
| 341 | + } |
| 342 | + |
| 343 | + //not in the map, or hard coded. What then? |
| 344 | + //Complain furiously, for your code is faulty. |
| 345 | + $msg = self::getGatewayName() . ': Requested value ' . $gateway_field_name . ' cannot be found in the transactions structure.'; |
| 346 | + self::log( $msg, LOG_CRIT ); |
| 347 | + throw new MWException( $msg ); |
| 348 | + } |
| 349 | + |
| 350 | + function buildRequestNameValueString() { |
| 351 | + $structure = $this->transactions[$this->currentTransaction()]['request']; |
| 352 | + if ( !is_array( $structure ) ) { |
| 353 | + return ''; |
| 354 | + } |
| 355 | + |
| 356 | + $queryvals = array( ); |
| 357 | + |
| 358 | + //we are going to assume a flat array, because... namevalue. |
| 359 | + foreach ( $structure as $fieldname ) { |
| 360 | + $fieldvalue = $this->getValue( $fieldname ); |
| 361 | + if ( $fieldvalue !== '' && $fieldvalue !== false ) { |
| 362 | + $queryvals[] = $fieldname . '[' . strlen( $fieldvalue ) . ']=' . $fieldvalue; |
| 363 | + } |
| 364 | + } |
| 365 | + |
| 366 | + $ret = implode( '&', $queryvals ); |
| 367 | + return $ret; |
| 368 | + } |
| 369 | + |
| 370 | + function buildRequestXML() { |
| 371 | + $this->xmlDoc = new DomDocument( '1.0' ); |
| 372 | + $node = $this->xmlDoc->createElement( 'XML' ); |
| 373 | + |
| 374 | + $structure = $this->transactions[$this->currentTransaction()]['request']; |
| 375 | + |
| 376 | + $this->buildTransactionNodes( $structure, $node ); |
| 377 | + $this->xmlDoc->appendChild( $node ); |
| 378 | + return $this->xmlDoc->saveXML(); |
| 379 | + } |
| 380 | + |
| 381 | + function buildTransactionNodes( $structure, &$node, $js = false ) { |
| 382 | + $transaction = $this->currentTransaction(); |
| 383 | + |
| 384 | + if ( !is_array( $structure ) ) { //this is a weird case that shouldn't ever happen. I'm just being... thorough. But, yeah: It's like... the base-1 case. |
| 385 | + $this->appendNodeIfValue( $structure, $node, $js ); |
| 386 | + } else { |
| 387 | + foreach ( $structure as $key => $value ) { |
| 388 | + if ( !is_array( $value ) ) { |
| 389 | + //do not use $key. $key is meaningless in this case. |
| 390 | + $this->appendNodeIfValue( $value, $node, $js ); |
| 391 | + } else { |
| 392 | + $keynode = $this->xmlDoc->createElement( $key ); |
| 393 | + $this->buildTransactionNodes( $value, $keynode, $js ); |
| 394 | + $node->appendChild( $keynode ); |
| 395 | + } |
| 396 | + } |
| 397 | + } |
| 398 | + //not actually returning anything. It's all side-effects. Because I suck like that. |
| 399 | + } |
| 400 | + |
| 401 | + function appendNodeIfValue( $value, &$node, $js = false ) { |
| 402 | + $nodevalue = $this->getValue( $value, $js ); |
| 403 | + if ( $nodevalue !== '' && $nodevalue !== false ) { |
| 404 | + $temp = $this->xmlDoc->createElement( $value, $nodevalue ); |
| 405 | + $node->appendChild( $temp ); |
| 406 | + } |
| 407 | + } |
| 408 | + |
| 409 | + //TODO: You can actually take this out if we never ever want to use ajax for a gateway. |
| 410 | + function buildTransactionFormat( $transaction ) { |
| 411 | + $this->currentTransaction( $transaction ); |
| 412 | + $this->xmlDoc = new DomDocument( '1.0' ); |
| 413 | + $node = $this->xmlDoc->createElement( 'XML' ); |
| 414 | + |
| 415 | + $structure = $this->transactions[$this->currentTransaction()]['request']; |
| 416 | + |
| 417 | + $this->buildTransactionNodes( $structure, $node, true ); |
| 418 | + $this->xmlDoc->appendChild( $node ); |
| 419 | + $xml = $this->xmlDoc->saveXML(); |
| 420 | + $xmlStart = strpos( $xml, "<XML>" ); |
| 421 | + self::log( "XML START" . $xmlStart ); |
| 422 | + $xml = substr( $xml, $xmlStart ); |
| 423 | + self::log( "XML stubby thing..." . $xml ); |
| 424 | + |
| 425 | + return $xml; |
| 426 | + } |
| 427 | + |
| 428 | + /** |
| 429 | + * Perform a transaction through the gateway |
| 430 | + * |
| 431 | + * @param $transaction string This is a specific transaction type like 'INSERT_ORDERWITHPAYMENT' |
| 432 | + * that maps to a first-level key in the $transactions array. |
| 433 | + */ |
| 434 | + function do_transaction( $transaction ) { |
| 435 | + try { |
| 436 | + $this->currentTransaction( $transaction ); |
| 437 | + //update the contribution tracking data |
| 438 | + $this->incrementNumAttempt(); |
| 439 | + |
| 440 | + //if we're supposed to add the donor data to the session, do that. |
| 441 | + if ( $this->transaction_option( 'addDonorDataToSession' ) ) { |
| 442 | + $this->addDonorDataToSession(); |
| 443 | + } |
| 444 | + |
| 445 | + $this->runPreProcess(); //many hooks get fired here... |
| 446 | + //TODO: Uhmmm... what if none of the validate hooks are enabled? |
| 447 | + //Currently, I think that means the transaction stops here, and that's not quite right. |
| 448 | + //...is it? |
| 449 | + // if the transaction was NOT flagged for processing by something in runPreProcess()... |
| 450 | + if ( $this->action != 'process' ) { |
| 451 | + self::log( "Transaction failed pre-process checks." . print_r( $this->getData(), true ) ); |
| 452 | + return array( |
| 453 | + 'status' => false, |
| 454 | + //TODO: appropriate messages. |
| 455 | + 'message' => "$transaction : Failed failed pre-process checks. Somebody PLEASE override me!", |
| 456 | + 'errors' => array( |
| 457 | + '1000000' => 'pre-process failed you.' //...stupid code. |
| 458 | + ), |
| 459 | + 'action' => $this->action, |
| 460 | + ); |
| 461 | + } |
| 462 | + |
| 463 | + // expose a hook for external handling of trxns ready for processing |
| 464 | + if ( $this->transaction_option( 'do_processhooks' ) ) { |
| 465 | + wfRunHooks( 'GatewayProcess', array( &$this ) ); //don't think anybody is using this yet, but you could! |
| 466 | + } |
| 467 | + |
| 468 | + $this->dataObj->updateContributionTracking( defined( 'OWA' ) ); |
| 469 | + |
| 470 | + if ( $this->getCommunicationType() === 'xml' ) { |
| 471 | + $this->getStopwatch( "buildRequestXML" ); |
| 472 | + $curlme = $this->buildRequestXML(); |
| 473 | + $this->saveCommunicationStats( "buildRequestXML", $transaction ); |
| 474 | + } |
| 475 | + |
| 476 | + if ( $this->getCommunicationType() === 'namevalue' ) { |
| 477 | + //buildRequestNameValueString() |
| 478 | + $this->getStopwatch( "buildRequestNameValueString" ); |
| 479 | + $curlme = $this->buildRequestNameValueString(); |
| 480 | + $this->saveCommunicationStats( "buildRequestNameValueString", $transaction ); |
| 481 | + } |
| 482 | + } catch ( MWException $e ) { |
| 483 | + self::log( "Malformed gateway definition. Cannot continue: Aborting.", LOG_CRIT ); |
| 484 | + return array( |
| 485 | + 'status' => false, |
| 486 | + //TODO: appropriate messages. |
| 487 | + 'message' => "$transaction : Malformed gateway definition. Cannot continue: Aborting.", |
| 488 | + 'errors' => array( |
| 489 | + '1000000' => 'Faulty Code! Bad programmer. Bad!' //...please change this. |
| 490 | + ), |
| 491 | + 'action' => $this->action, |
| 492 | + ); |
| 493 | + } |
| 494 | + |
| 495 | + //start looping here, if we're the sort of transaction that needs to do that. |
| 496 | + $stopflag = false; |
| 497 | + $counter = 0; |
| 498 | + $statuses = $this->transaction_option( 'loop_for_status' ); |
| 499 | + $this->getStopwatch( __FUNCTION__, true ); |
| 500 | + while ( $stopflag === false ) { |
| 501 | + $stopflag = true; |
| 502 | + $counter += 1; |
| 503 | + $txn_ok = $this->curl_transaction( $curlme ); |
| 504 | + |
| 505 | + if ( $txn_ok === true ) { //We have something to slice and dice. |
| 506 | + self::log( "RETURNED FROM CURL:" . print_r( $this->getTransactionAllResults(), true ) ); |
| 507 | + |
| 508 | + //set the status of the response. This is the COMMUNICATION status, and has nothing |
| 509 | + //to do with the result of the transaction. |
| 510 | + $formatted = $this->getFormattedResponse( $this->getTransactionRawResponse() ); |
| 511 | + $this->setTransactionResult( $this->getResponseStatus( $formatted ), 'status' ); |
| 512 | + |
| 513 | + //set errors |
| 514 | + //TODO: This "errors" business is becoming a bit of a misnomer, as the result code and message |
| 515 | + //are frequently packaged togther in the same place, whether the transaction passed or failed. |
| 516 | + $this->setTransactionResult( $this->getResponseErrors( $formatted ), 'errors' ); |
| 517 | + |
| 518 | + //if we're still okay (hey, even if we're not), get relevent dataz. |
| 519 | + $pulled_data = $this->getResponseData( $formatted ); |
| 520 | + $this->setTransactionResult( $pulled_data, 'data' ); |
| 521 | + |
| 522 | + //TODO: Death to the pulled_data parameter! |
| 523 | + $this->processResponse( $pulled_data ); //now we've set all the transaction results... |
| 524 | + } else { |
| 525 | + self::log( "Transaction Communication failed" . print_r( $this->getTransactionAllResults(), true ) ); |
| 526 | + } |
| 527 | + |
| 528 | + if ( is_array( $statuses ) ) { //only then will we consider doing this again. |
| 529 | + if ( $this->getStopwatch( __FUNCTION__ ) < self::getGlobal( "RetrySeconds" ) ) { |
| 530 | + if ( $txn_ok === false ) { |
| 531 | + $stopflag = false; |
| 532 | + } else { |
| 533 | + if ( !in_array( $this->getTransactionWMFStatus(), $statuses ) ) { |
| 534 | + $stopflag = false; |
| 535 | + } |
| 536 | + } |
| 537 | + } |
| 538 | + } |
| 539 | + } |
| 540 | + |
| 541 | + //Log out how many times we looped, and what the clock is now. |
| 542 | + $this->saveCommunicationStats( __FUNCTION__, $transaction, "counter = $counter" ); |
| 543 | + |
| 544 | + if ( $txn_ok === false ) { //nothing to process, so we have to build it manually |
| 545 | + return array( |
| 546 | + 'status' => false, |
| 547 | + 'message' => "$transaction Communication Failed!", |
| 548 | + 'errors' => array( |
| 549 | + '1000000' => 'communication failure' //...stupid code. |
| 550 | + ), |
| 551 | + ); |
| 552 | + } |
| 553 | + |
| 554 | + // expose a hook for any post processing |
| 555 | + if ( $this->transaction_option( 'do_processhooks' ) ) { |
| 556 | + wfRunHooks( 'GatewayPostProcess', array( &$this ) ); //conversion log (at least) |
| 557 | + $this->doStompTransaction(); |
| 558 | + } |
| 559 | + |
| 560 | + $this->dataObj->unsetEditToken(); |
| 561 | + |
| 562 | + //TODO: Actually pull these from somewhere legit. |
| 563 | + if ( $this->getTransactionStatus() === true ) { |
| 564 | + $this->setTransactionResult( "$transaction Transaction Successful!", 'message' ); |
| 565 | + } elseif ( $this->getTransactionStatus() === false ) { |
| 566 | + $this->setTransactionResult( "$transaction Transaction FAILED!", 'message' ); |
| 567 | + } else { |
| 568 | + $this->setTransactionResult( "$transaction Transaction... weird. I have no idea what happened there.", 'message' ); |
| 569 | + } |
| 570 | + |
| 571 | + // log that the transaction is essentially complete |
| 572 | + self::log( $this->getData( 'order_id' ) . " Transaction complete." ); |
| 573 | + |
| 574 | + //if we're not actively adding the donor data to the session, kill it. |
| 575 | + if ( !$this->transaction_option( 'addDonorDataToSession' ) ) { |
| 576 | + $this->unsetAllGatewaySessionData(); |
| 577 | + } |
| 578 | + |
| 579 | + return $this->getTransactionAllResults(); |
| 580 | + |
| 581 | + //speaking of universal form: |
| 582 | + //$result['status'] = something I wish could be boiled down to a bool, but that's way too optimistic, I think. |
| 583 | + //$result['message'] = whatever we want to display back? |
| 584 | + //$result['errors']['code']['message'] = |
| 585 | + //$result['data'][$whatever] = values they pass back to us for whatever reason. We might... log it, or pieces of it, or something? |
| 586 | + } |
| 587 | + |
| 588 | + function getCurlBaseOpts() { |
| 589 | + //I chose to return this as a function so it's easy to override. |
| 590 | + //TODO: probably this for all the junk I currently have stashed in the constructor. |
| 591 | + //...maybe. |
| 592 | + $opts = array( |
| 593 | + CURLOPT_URL => $this->url, |
| 594 | + CURLOPT_USERAGENT => Http::userAgent(), |
| 595 | + CURLOPT_HEADER => 1, |
| 596 | + CURLOPT_RETURNTRANSFER => 1, |
| 597 | + CURLOPT_TIMEOUT => self::getGlobal( 'Timeout' ), |
| 598 | + CURLOPT_FOLLOWLOCATION => 0, |
| 599 | + CURLOPT_SSL_VERIFYPEER => 0, |
| 600 | + CURLOPT_SSL_VERIFYHOST => 2, |
| 601 | + CURLOPT_FORBID_REUSE => true, |
| 602 | + CURLOPT_POST => 1, |
| 603 | + ); |
| 604 | + |
| 605 | + // set proxy settings if necessary |
| 606 | + if ( self::getGlobal( 'UseHTTPProxy' ) ) { |
| 607 | + $opts[CURLOPT_HTTPPROXYTUNNEL] = 1; |
| 608 | + $opts[CURLOPT_PROXY] = self::getGlobal( 'HTTPProxy' ); |
| 609 | + } |
| 610 | + return $opts; |
| 611 | + } |
| 612 | + |
| 613 | + function getCurlBaseHeaders() { |
| 614 | + $headers = array( |
| 615 | + 'Content-Type: text/' . $this->getCommunicationType() . '; charset=utf-8', |
| 616 | + 'X-VPS-Client-Timeout: 45', |
| 617 | + 'X-VPS-Request-ID:' . $this->postdatadefaults['order_id'], |
| 618 | + ); |
| 619 | + return $headers; |
| 620 | + } |
| 621 | + |
| 622 | + protected function currentTransaction( $transaction = '' ) { //get&set in one! |
| 623 | + static $current_transaction; |
| 624 | + if ( $transaction != '' ) { |
| 625 | + $current_transaction = $transaction; |
| 626 | + } |
| 627 | + if ( !isset( $current_transaction ) ) { |
| 628 | + return false; |
| 629 | + } |
| 630 | + if ( empty( $this->transactions ) || !is_array( $this->transactions ) || !array_key_exists( $current_transaction, $this->transactions ) ) { |
| 631 | + $msg = self::getGatewayName() . ': Transactions structure is malformed! ' . $current_transaction . ' transaction cannot be constructed.'; |
| 632 | + self::log( $msg, LOG_CRIT ); |
| 633 | + throw new MWException( $msg ); |
| 634 | + } |
| 635 | + return $current_transaction; |
| 636 | + } |
| 637 | + |
| 638 | + /** |
| 639 | + * Sends a name-value pair string to Payflow gateway |
| 640 | + * |
| 641 | + * @param $data String: The exact thing we want to send. |
| 642 | + */ |
| 643 | + protected function curl_transaction( $data ) { |
| 644 | + // assign header data necessary for the curl_setopt() function |
| 645 | + $this->getStopwatch( __FUNCTION__, true ); |
| 646 | + |
| 647 | + $ch = curl_init(); |
| 648 | + |
| 649 | + $headers = $this->getCurlBaseHeaders(); |
| 650 | + $headers[] = 'Content-Length: ' . strlen( $data ); |
| 651 | + |
| 652 | + if ( $this->getCommunicationType() === 'xml' ) { |
| 653 | + self::log( "Sending Data: " . $this->formatXmlString( $data ) ); |
| 654 | + } else { |
| 655 | + self::log( "Sending Data: " . $data ); |
| 656 | + } |
| 657 | + |
| 658 | + $curl_opts = $this->getCurlBaseOpts(); |
| 659 | + $curl_opts[CURLOPT_HTTPHEADER] = $headers; |
| 660 | + $curl_opts[CURLOPT_POSTFIELDS] = $data; |
| 661 | + |
| 662 | + foreach ( $curl_opts as $option => $value ) { |
| 663 | + curl_setopt( $ch, $option, $value ); |
| 664 | + } |
| 665 | + |
| 666 | + // As suggested in the PayPal developer forum sample code, try more than once to get a response |
| 667 | + // in case there is a general network issue |
| 668 | + $i = 1; |
| 669 | + |
| 670 | + $results = array( ); |
| 671 | + |
| 672 | + while ( $i++ <= 3 ) { |
| 673 | + self::log( $this->postdatadefaults['order_id'] . ' Preparing to send transaction to ' . self::getGatewayName() ); |
| 674 | + $results['result'] = curl_exec( $ch ); |
| 675 | + $results['headers'] = curl_getinfo( $ch ); |
| 676 | + |
| 677 | + if ( $results['headers']['http_code'] != 200 && $results['headers']['http_code'] != 403 ) { |
| 678 | + self::log( $this->postdatadefaults['order_id'] . ' Failed sending transaction to ' . self::getGatewayName() . ', retrying' ); |
| 679 | + sleep( 1 ); |
| 680 | + } elseif ( $results['headers']['http_code'] == 200 || $results['headers']['http_code'] == 403 ) { |
| 681 | + self::log( $this->postdatadefaults['order_id'] . ' Finished sending transaction to ' . self::getGatewayName() ); |
| 682 | + break; |
| 683 | + } |
| 684 | + } |
| 685 | + |
| 686 | + $this->saveCommunicationStats( __FUNCTION__, $this->currentTransaction(), "Request:" . print_r( $data, true ) . "\nResponse" . print_r( $results, true ) ); |
| 687 | + |
| 688 | + if ( $results['headers']['http_code'] != 200 ) { |
| 689 | + $results['result'] = false; |
| 690 | + //TODO: i18n here! |
| 691 | + //TODO: But also, fire off some kind of "No response from the gateway" thing to somebody so we know right away. |
| 692 | + $results['message'] = 'No response from ' . self::getGatewayName() . '. Please try again later!'; |
| 693 | + $when = time(); |
| 694 | + self::log( $this->postdatadefaults['order_id'] . ' No response from ' . self::getGatewayName() . ': ' . curl_error( $ch ) ); |
| 695 | + curl_close( $ch ); |
| 696 | + return false; |
| 697 | + } |
| 698 | + |
| 699 | + curl_close( $ch ); |
| 700 | + |
| 701 | + $this->setTransactionResult( $results ); |
| 702 | + return true; |
| 703 | + } |
| 704 | + |
| 705 | + function stripXMLResponseHeaders( $rawResponse ) { |
| 706 | + $xmlStart = strpos( $rawResponse, '<?xml' ); |
| 707 | + if ( $xmlStart == false ) { //I totally saw this happen one time. No XML, just <RESPONSE>... |
| 708 | + $xmlStart = strpos( $rawResponse, '<RESPONSE' ); |
| 709 | + } |
| 710 | + if ( $xmlStart == false ) { //Still false. Your Head Asplode. |
| 711 | + self::log( "Wow, that was so messed up I couldn't even parse the response, so here's the thing in its entirety:\n" . $rawResponse ); |
| 712 | + return false; |
| 713 | + } |
| 714 | + $justXML = substr( $rawResponse, $xmlStart ); |
| 715 | + $this->setTransactionResult( $justXML, 'unparsed_data' ); |
| 716 | + return $justXML; |
| 717 | + } |
| 718 | + |
| 719 | + function stripNameValueResponseHeaders( $rawResponse ) { |
| 720 | + $result = strstr( $rawResponse, 'RESULT' ); |
| 721 | + $this->setTransactionResult( $result, 'unparsed_data' ); |
| 722 | + return $result; |
| 723 | + } |
| 724 | + |
| 725 | + public static function log( $msg, $log_level=LOG_INFO, $log_id_suffix = '' ) { |
| 726 | + $identifier = self::getIdentifier() . "_gateway" . $log_id_suffix; |
| 727 | + |
| 728 | + // if we're not using the syslog facility, use wfDebugLog |
| 729 | + if ( !self::getGlobal( 'UseSyslog' ) ) { |
| 730 | + wfDebugLog( $identifier, $msg ); |
| 731 | + return; |
| 732 | + } |
| 733 | + |
| 734 | + // otherwise, use syslogging |
| 735 | + openlog( $identifier, LOG_ODELAY, LOG_SYSLOG ); |
| 736 | + syslog( $log_level, $msg ); |
| 737 | + closelog(); |
| 738 | + } |
| 739 | + |
| 740 | + //To avoid reinventing the wheel: taken from http://recursive-design.com/blog/2007/04/05/format-xml-with-php/ |
| 741 | + function formatXmlString( $xml ) { |
| 742 | + // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries) |
| 743 | + $xml = preg_replace( '/(>)(<)(\/*)/', "$1\n$2$3", $xml ); |
| 744 | + |
| 745 | + // now indent the tags |
| 746 | + $token = strtok( $xml, "\n" ); |
| 747 | + $result = ''; // holds formatted version as it is built |
| 748 | + $pad = 0; // initial indent |
| 749 | + $matches = array( ); // returns from preg_matches() |
| 750 | + // scan each line and adjust indent based on opening/closing tags |
| 751 | + while ( $token !== false ) : |
| 752 | + |
| 753 | + // test for the various tag states |
| 754 | + // 1. open and closing tags on same line - no change |
| 755 | + if ( preg_match( '/.+<\/\w[^>]*>$/', $token, $matches ) ) : |
| 756 | + $indent = 0; |
| 757 | + // 2. closing tag - outdent now |
| 758 | + elseif ( preg_match( '/^<\/\w/', $token, $matches ) ) : |
| 759 | + $pad--; |
| 760 | + // 3. opening tag - don't pad this one, only subsequent tags |
| 761 | + elseif ( preg_match( '/^<\w[^>]*[^\/]>.*$/', $token, $matches ) ) : |
| 762 | + $indent = 1; |
| 763 | + // 4. no indentation needed |
| 764 | + else : |
| 765 | + $indent = 0; |
| 766 | + endif; |
| 767 | + |
| 768 | + // pad the line with the required number of leading spaces |
| 769 | + $line = str_pad( $token, strlen( $token ) + $pad, ' ', STR_PAD_LEFT ); |
| 770 | + $result .= $line . "\n"; // add to the cumulative result, with linefeed |
| 771 | + $token = strtok( "\n" ); // get the next token |
| 772 | + $pad += $indent; // update the pad size for subsequent lines |
| 773 | + endwhile; |
| 774 | + |
| 775 | + return $result; |
| 776 | + } |
| 777 | + |
| 778 | + static function getCommunicationType() { |
| 779 | + $c = get_called_class(); |
| 780 | + return $c::COMMUNICATION_TYPE; |
| 781 | + } |
| 782 | + |
| 783 | + static function getGatewayName() { |
| 784 | + $c = get_called_class(); |
| 785 | + return $c::GATEWAY_NAME; |
| 786 | + } |
| 787 | + |
| 788 | + static function getGlobalPrefix() { |
| 789 | + $c = get_called_class(); |
| 790 | + return $c::GLOBAL_PREFIX; |
| 791 | + } |
| 792 | + |
| 793 | + static function getIdentifier() { |
| 794 | + $c = get_called_class(); |
| 795 | + return $c::IDENTIFIER; |
| 796 | + } |
| 797 | + |
| 798 | + /** |
| 799 | + * getStopwatch keeps track of how long things take, for logging, |
| 800 | + * output, determining if we should loop on some method again... whatever. |
| 801 | + * @staticvar array $start The microtime at which a stopwatch was started. |
| 802 | + * @param string $string Some identifier for each stopwatch value we want to |
| 803 | + * keep. Each unique $string passed in will get its own value in $start. |
| 804 | + * @param bool $reset If this is set to true, it will reset any $start value |
| 805 | + * recorded for the $string identifier. |
| 806 | + * @return numeric The difference in microtime (rounded to 4 decimal places) |
| 807 | + * between the $start value, and now. |
| 808 | + */ |
| 809 | + public function getStopwatch( $string, $reset = false ) { |
| 810 | + static $start = array( ); |
| 811 | + $now = microtime( true ); |
| 812 | + |
| 813 | + if ( empty( $start ) || !array_key_exists( $string, $start ) || $reset === true ) { |
| 814 | + $start[$string] = $now; |
| 815 | + } |
| 816 | + $clock = round( $now - $start[$string], 4 ); |
| 817 | + self::log( "\nClock at $string: $clock ($now)" ); |
| 818 | + return $clock; |
| 819 | + } |
| 820 | + |
| 821 | + /** |
| 822 | + * |
| 823 | + * @param type $function |
| 824 | + * @param type $additional |
| 825 | + * @param type $vars |
| 826 | + */ |
| 827 | + function saveCommunicationStats( $function = '', $additional = '', $vars = '' ) { |
| 828 | + $params = array( ); |
| 829 | + if ( self::getGlobal( 'SaveCommStats' ) ) { |
| 830 | + $db = ContributionTrackingProcessor::contributionTrackingConnection(); |
| 831 | + |
| 832 | + //TODO: Actually define this table somewhere in the code, once we |
| 833 | + //are reasonably certain we know what we want to see in it. |
| 834 | + if ( !( $db->tableExists( 'communication_stats' ) ) ) { |
| 835 | + return; |
| 836 | + } |
| 837 | + |
| 838 | + $params['contribution_id'] = $this->dataObj->getVal( 'contribution_tracking_id' ); |
| 839 | + $params['ts'] = $db->timestamp(); |
| 840 | + $params['duration'] = $this->getStopwatch( __FUNCTION__ ); |
| 841 | + $params['gateway'] = self::getGatewayName(); |
| 842 | + $params['function'] = $function; |
| 843 | + $params['vars'] = $vars; |
| 844 | + $params['additional'] = $additional; |
| 845 | + |
| 846 | + $db->insert( 'communication_stats', $params ); |
| 847 | + } |
| 848 | + } |
| 849 | + |
| 850 | + function xmlChildrenToArray( $xml, $nodename ) { |
| 851 | + $data = array( ); |
| 852 | + foreach ( $xml->getElementsByTagName( $nodename ) as $node ) { |
| 853 | + foreach ( $node->childNodes as $childnode ) { |
| 854 | + if ( trim( $childnode->nodeValue ) != '' ) { |
| 855 | + $data[$childnode->nodeName] = $childnode->nodeValue; |
| 856 | + } |
| 857 | + } |
| 858 | + } |
| 859 | + return $data; |
| 860 | + } |
| 861 | + |
| 862 | + /** |
| 863 | + * DO NOT DEFINE OVERLAPPING RANGES! |
| 864 | + * TODO: Make sure it won't let you add overlapping ranges. That would probably necessitate the sort moving to here, too. |
| 865 | + * @param type $transaction |
| 866 | + * @param type $key |
| 867 | + * @param type $action |
| 868 | + * @param type $lower |
| 869 | + * @param type $upper |
| 870 | + */ |
| 871 | + function addCodeRange( $transaction, $key, $action, $lower, $upper = null ) { |
| 872 | + if ( $upper === null ) { |
| 873 | + $this->return_value_map[$transaction][$key][$lower] = $action; |
| 874 | + } else { |
| 875 | + $this->return_value_map[$transaction][$key][$upper] = array( 'action' => $action, 'lower' => $lower ); |
| 876 | + } |
| 877 | + } |
| 878 | + |
| 879 | + function findCodeAction( $transaction, $key, $code ) { |
| 880 | + $this->getStopwatch( __FUNCTION__, true ); |
| 881 | + if ( !array_key_exists( $transaction, $this->return_value_map ) || !array_key_exists( $key, $this->return_value_map[$transaction] ) ) { |
| 882 | + return null; |
| 883 | + } |
| 884 | + if ( !is_array( $this->return_value_map[$transaction][$key] ) ) { |
| 885 | + return null; |
| 886 | + } |
| 887 | + //sort the array so we can do this quickly. |
| 888 | + ksort( $this->return_value_map[$transaction][$key], SORT_NUMERIC ); |
| 889 | + |
| 890 | + $ranges = $this->return_value_map[$transaction][$key]; |
| 891 | + //so, you have a code, which is a number. You also have a numerically sorted array. |
| 892 | + //loop through until you find an upper >= your code. |
| 893 | + //make sure it's in the range, and return the action. |
| 894 | + foreach ( $ranges as $upper => $val ) { |
| 895 | + if ( $upper >= $code ) { //you've arrived. It's either here or it's nowhere. |
| 896 | + if ( is_array( $val ) ) { |
| 897 | + if ( $val['lower'] <= $code ) { |
| 898 | + $this->saveCommunicationStats( __FUNCTION__, $transaction, "code = $code" ); |
| 899 | + return $val['action']; |
| 900 | + } else { |
| 901 | + return null; |
| 902 | + } |
| 903 | + } else { |
| 904 | + if ( $upper === $code ) { |
| 905 | + $this->saveCommunicationStats( __FUNCTION__, $transaction, "code = $code" ); |
| 906 | + return $val; |
| 907 | + } else { |
| 908 | + return null; |
| 909 | + } |
| 910 | + } |
| 911 | + } |
| 912 | + } |
| 913 | + //if we walk straight off the end... |
| 914 | + return null; |
| 915 | + } |
| 916 | + |
| 917 | + function addDonorDataToSession() { |
| 918 | + $this->dataObj->addDonorDataToSession(); |
| 919 | + } |
| 920 | + |
| 921 | + function unsetAllGatewaySessionData() { |
| 922 | + $this->dataObj->unsetAllDDSessionData(); |
| 923 | + } |
| 924 | + |
| 925 | + function doStompTransaction() { |
| 926 | + $this->debugarray[] = "Attempting Stomp Transaction!"; |
| 927 | + $hook = ''; |
| 928 | + |
| 929 | + $status = $this->getTransactionWMFStatus(); |
| 930 | + switch ( $status ) { |
| 931 | + case 'complete': |
| 932 | + $hook = 'gwStomp'; |
| 933 | + break; |
| 934 | + case 'pending': |
| 935 | + case 'pending-poke': |
| 936 | + $hook = 'gwPendingStomp'; |
| 937 | + break; |
| 938 | + } |
| 939 | + if ( $hook === '' ) { |
| 940 | + $this->debugarray[] = "No Stomp Hook Found for WMF_Status $status"; |
| 941 | + return; |
| 942 | + } |
| 943 | + |
| 944 | + $data = $this->getTransactionData(); |
| 945 | + |
| 946 | + //Gah. I might want to move all this data prep business upstream more than somewhat. |
| 947 | + //...but that's for later. |
| 948 | + // Add the session vars to the data object |
| 949 | + if ( $this->transaction_option( 'pullDonorDataFromSession' ) ) { |
| 950 | + $this->dataObj->populateDonorFromSession(); |
| 951 | + } |
| 952 | + |
| 953 | + // refresh our data |
| 954 | + $this->postdata = $this->dataObj->getData(); |
| 955 | + |
| 956 | + // stage the gateway data |
| 957 | + $this->stageData( 'response' ); |
| 958 | + |
| 959 | + // send the thing. |
| 960 | + $transaction = array( |
| 961 | + 'response' => $this->getTransactionMessage(), |
| 962 | + 'date' => time(), |
| 963 | + 'gateway_txn_id' => $this->getTransactionGatewayTxnID(), |
| 964 | + 'language' => '', |
| 965 | + ); |
| 966 | + $transaction += $this->getData(); |
| 967 | + |
| 968 | + self::log( "Intended STOMP transaction: " . print_r( $transaction, true ) ); |
| 969 | + |
| 970 | + try { |
| 971 | + wfRunHooks( $hook, array( $transaction ) ); |
| 972 | + } catch ( Exception $e ) { |
| 973 | + self::log( "STOMP ERROR. Could not add message. " . $e->getMessage() , LOG_CRIT ); |
| 974 | + } |
| 975 | + |
| 976 | + } |
| 977 | + |
| 978 | + function smooshVarsForStaging() { |
| 979 | + |
| 980 | + foreach ( $this->staged_vars as $field ) { |
| 981 | + if ( !array_key_exists( $field, $this->postdata ) || empty( $this->postdata[$field] ) ) { |
| 982 | + if ( array_key_exists( $field, $this->postdatadefaults ) ) { |
| 983 | + $this->postdata[$field] = $this->postdatadefaults[$field]; |
| 984 | + } |
| 985 | + } |
| 986 | + //what do we do in the event that we're still nothing? (just move on.) |
| 987 | + } |
| 988 | + } |
| 989 | + |
| 990 | + /** |
| 991 | + * |
| 992 | + * @param type $type Whatever types of staging you feel like having in your child class. |
| 993 | + * ...but usually request and response. I think. |
| 994 | + */ |
| 995 | + function stageData( $type = 'request' ) { |
| 996 | + $this->defineStagedVars(); |
| 997 | + $this->smooshVarsForStaging(); //yup, we do need to do this seperately. |
| 998 | + //If we tried to piggyback off the same loop, all the vars wouldn't be ready, and some staging functions will require |
| 999 | + //multiple variables. |
| 1000 | + foreach ( $this->staged_vars as $field ) { |
| 1001 | + $function_name = 'stage_' . $field; |
| 1002 | + if ( method_exists( $this, $function_name ) ) { |
| 1003 | + $this->{$function_name}( $type ); |
| 1004 | + } |
| 1005 | + } |
| 1006 | + } |
| 1007 | + |
| 1008 | + function getPaypalRedirectURL() { |
| 1009 | + $utm_source = $this->getData( 'utm_source' ); |
| 1010 | + |
| 1011 | + // update the utm source to set the payment instrument to pp rather than cc |
| 1012 | + $utm_source_parts = explode( ".", $utm_source ); |
| 1013 | + $utm_source_parts[2] = 'pp'; |
| 1014 | + $data['utm_source'] = implode( ".", $utm_source_parts ); |
| 1015 | + $data['gateway'] = 'paypal'; |
| 1016 | + $data['currency_code'] = $data['currency']; |
| 1017 | + |
| 1018 | + // Add our response vars to the data object. |
| 1019 | + $this->dataObj->addData( $data ); |
| 1020 | + // refresh our data |
| 1021 | + $this->postdata = $this->dataObj->getData(); |
| 1022 | + |
| 1023 | + //update contribution tracking |
| 1024 | + $this->dataObj->updateContributionTracking( true ); |
| 1025 | + |
| 1026 | + $ret = self::getGlobal( "PaypalURL" ) . "/" . $this->postdata['language'] . "?gateway=paypal&" . http_build_query( $this->getPaypalData() ); |
| 1027 | + self::log( $ret ); |
| 1028 | + return $ret; |
| 1029 | + } |
| 1030 | + |
| 1031 | + protected function getPaypalData() { |
| 1032 | + $paypalkeys = array( |
| 1033 | + 'gateway', |
| 1034 | + 'contribution_tracking_id', |
| 1035 | + 'comment', |
| 1036 | + 'referrer', |
| 1037 | + 'comment-option', |
| 1038 | + 'utm_source', |
| 1039 | + 'utm_medium', |
| 1040 | + 'utm_campaign', |
| 1041 | + 'email-opt', |
| 1042 | + 'language', |
| 1043 | + 'owa_session', |
| 1044 | + 'owa_ref', |
| 1045 | + 'tshirt', |
| 1046 | + 'returnto', |
| 1047 | + 'currency_code', |
| 1048 | + 'fname', |
| 1049 | + 'lname', |
| 1050 | + 'email', |
| 1051 | + 'address1', |
| 1052 | + 'city', |
| 1053 | + 'state', |
| 1054 | + 'zip', |
| 1055 | + 'country', |
| 1056 | + 'address_override', |
| 1057 | + 'recurring_paypal', |
| 1058 | + 'amount', |
| 1059 | + 'amountGiven', |
| 1060 | + 'size', |
| 1061 | + 'premium_language', |
| 1062 | + ); |
| 1063 | + $ret = array(); |
| 1064 | + foreach ( $paypalkeys as $key ){ |
| 1065 | + $val = $this->getData( $key ); |
| 1066 | + if (!is_null( $val )){ |
| 1067 | + $ret[$key] = $this->getData( $key ); |
| 1068 | + } |
| 1069 | + } |
| 1070 | + return $ret; |
| 1071 | + } |
| 1072 | + |
| 1073 | + public function getTransactionAllResults() { |
| 1074 | + if ( !empty( $this->transaction_results ) && is_array( $this->transaction_results ) ) { |
| 1075 | + return $this->transaction_results; |
| 1076 | + } else { |
| 1077 | + return false; |
| 1078 | + } |
| 1079 | + } |
| 1080 | + |
| 1081 | + /** |
| 1082 | + * SetTransactionResult sets the gateway adapter object's |
| 1083 | + * $transaction_results value. |
| 1084 | + * If a $key is specified, it only sets the specified key's value. If no |
| 1085 | + * $key is specified, it resets the value of the entire array. |
| 1086 | + * @param mixed $value The value to set in $transaction_results |
| 1087 | + * @param mixed $key Optional: A specific key to set, or false (default) to |
| 1088 | + * reset the entire result array. |
| 1089 | + */ |
| 1090 | + public function setTransactionResult( $value, $key = false ) { |
| 1091 | + if ( $key === false ) { |
| 1092 | + $this->transaction_results = $value; |
| 1093 | + } else { |
| 1094 | + $this->transaction_results[$key] = $value; |
| 1095 | + } |
| 1096 | + } |
| 1097 | + |
| 1098 | + public function getTransactionRawResponse() { |
| 1099 | + if ( array_key_exists( 'result', $this->transaction_results ) ) { |
| 1100 | + return $this->transaction_results['result']; |
| 1101 | + } else { |
| 1102 | + return false; |
| 1103 | + } |
| 1104 | + } |
| 1105 | + |
| 1106 | + /** |
| 1107 | + * If it has been set: returns the Transaction Status in the |
| 1108 | + * $transaction_results array. Otherwise, returns false. |
| 1109 | + * @return mixed Transaction results status, or false if not set. |
| 1110 | + */ |
| 1111 | + public function getTransactionStatus() { |
| 1112 | + if ( array_key_exists( 'status', $this->transaction_results ) ) { |
| 1113 | + return $this->transaction_results['status']; |
| 1114 | + } else { |
| 1115 | + return false; |
| 1116 | + } |
| 1117 | + } |
| 1118 | + |
| 1119 | + /** |
| 1120 | + * If it has been set: returns the WMF Transaction Status in the |
| 1121 | + * $transaction_results array. This is the one we care about for switching |
| 1122 | + * on overall behavior. Otherwise, returns false. |
| 1123 | + * @return mixed WMF Transaction results status, or false if not set. |
| 1124 | + */ |
| 1125 | + public function getTransactionWMFStatus() { |
| 1126 | + if ( array_key_exists( 'WMF_STATUS', $this->transaction_results ) ) { |
| 1127 | + return $this->transaction_results['WMF_STATUS']; |
| 1128 | + } else { |
| 1129 | + return false; |
| 1130 | + } |
| 1131 | + } |
| 1132 | + |
| 1133 | + /** |
| 1134 | + * Sets the WMF Transaction Status. This is the one we care about for |
| 1135 | + * switching on behavior. |
| 1136 | + */ |
| 1137 | + public function setTransactionWMFStatus( $status ) { |
| 1138 | + $this->transaction_results['WMF_STATUS'] = $status; |
| 1139 | + } |
| 1140 | + |
| 1141 | + public function getTransactionMessage() { |
| 1142 | + if ( array_key_exists( 'txn_message', $this->transaction_results ) ) { |
| 1143 | + return $this->transaction_results['txn_message']; |
| 1144 | + } else { |
| 1145 | + return false; |
| 1146 | + } |
| 1147 | + } |
| 1148 | + |
| 1149 | + public function getTransactionGatewayTxnID() { |
| 1150 | + if ( array_key_exists( 'gateway_txn_id', $this->transaction_results ) ) { |
| 1151 | + return $this->transaction_results['gateway_txn_id']; |
| 1152 | + } else { |
| 1153 | + return false; |
| 1154 | + } |
| 1155 | + } |
| 1156 | + |
| 1157 | + /** |
| 1158 | + * Returns the FORMATTED data harvested from the reply, or false if it is not set. |
| 1159 | + * @return mixed An array of returned data, or false. |
| 1160 | + */ |
| 1161 | + public function getTransactionData() { |
| 1162 | + if ( array_key_exists( 'data', $this->transaction_results ) ) { |
| 1163 | + return $this->transaction_results['data']; |
| 1164 | + } else { |
| 1165 | + return false; |
| 1166 | + } |
| 1167 | + } |
| 1168 | + |
| 1169 | + public function setFormClass( $formClassName ) { |
| 1170 | + //I'm adding this because Captcha needs it, and we're gonna fire the hook inside. Nothing else really needs it as far as I know. |
| 1171 | + $this->form_class = $formClassName; |
| 1172 | + } |
| 1173 | + |
| 1174 | + public function getFormClass() { |
| 1175 | + if ( isset( $this->form_class ) && class_exists( $this->form_class ) ) { |
| 1176 | + return $this->form_class; |
| 1177 | + } else { |
| 1178 | + return false; |
| 1179 | + } |
| 1180 | + } |
| 1181 | + |
| 1182 | + public function getGatewayAdapterClass() { |
| 1183 | + return get_called_class(); |
| 1184 | + } |
| 1185 | + |
| 1186 | + public function setValidationErrors( $errors ) { |
| 1187 | + $this->validation_errors = $errors; |
| 1188 | + } |
| 1189 | + |
| 1190 | + public function getValidationErrors() { |
| 1191 | + if ( !empty( $this->validation_errors ) ) { |
| 1192 | + return $this->validation_errors; |
| 1193 | + } else { |
| 1194 | + return false; |
| 1195 | + } |
| 1196 | + } |
| 1197 | + |
| 1198 | + public function incrementNumAttempt() { |
| 1199 | + $this->dataObj->incrementNumAttempt(); |
| 1200 | + } |
| 1201 | + |
| 1202 | + public function setHash( $hashval ) { |
| 1203 | + $this->dataObj->setVal( 'data_hash', $hashval ); |
| 1204 | + } |
| 1205 | + |
| 1206 | + public function unsetHash() { |
| 1207 | + $this->dataObj->expunge( 'data_hash' ); |
| 1208 | + } |
| 1209 | + |
| 1210 | + public function setActionHash( $hashval ) { |
| 1211 | + $this->dataObj->setVal( 'action', $hashval ); |
| 1212 | + } |
| 1213 | + |
| 1214 | + public function unsetActionHash() { |
| 1215 | + $this->dataObj->expunge( 'action' ); |
| 1216 | + } |
| 1217 | + |
| 1218 | + function runPreProcess() { |
| 1219 | + if ( $this->transaction_option( 'do_validation' ) ) { |
| 1220 | + if ( !isset( $wgHooks['GatewayValidate'] ) ) { |
| 1221 | + //if there ARE no validate hooks, we're okay. |
| 1222 | + $this->action = 'process'; |
| 1223 | + return; |
| 1224 | + } |
| 1225 | + // allow any external validators to have their way with the data |
| 1226 | + self::log( $this->getData( 'order_id' ) . " Preparing to query MaxMind" ); |
| 1227 | + wfRunHooks( 'GatewayValidate', array( &$this ) ); |
| 1228 | + self::log( $this->getData( 'order_id' ) . ' Finished querying Maxmind' ); |
| 1229 | + |
| 1230 | + // if the transaction was flagged for review |
| 1231 | + if ( $this->action == 'review' ) { |
| 1232 | + // expose a hook for external handling of trxns flagged for review |
| 1233 | + wfRunHooks( 'GatewayReview', array( &$this ) ); |
| 1234 | + } |
| 1235 | + |
| 1236 | + // if the transaction was flagged to be 'challenged' |
| 1237 | + if ( $this->action == 'challenge' ) { |
| 1238 | + // expose a hook for external handling of trxns flagged for challenge (eg captcha) |
| 1239 | + wfRunHooks( 'GatewayChallenge', array( &$this ) ); |
| 1240 | + } |
| 1241 | + |
| 1242 | + // if the transaction was flagged for rejection |
| 1243 | + if ( $this->action == 'reject' ) { |
| 1244 | + // expose a hook for external handling of trxns flagged for rejection |
| 1245 | + wfRunHooks( 'GatewayReject', array( &$this ) ); |
| 1246 | + $this->dataObj->unsetEditToken(); |
| 1247 | + } |
| 1248 | + } else { |
| 1249 | + $this->action = 'process'; //we have to do this so do_transaction doesn't kick out. |
| 1250 | + } |
| 1251 | + } |
| 1252 | + |
| 1253 | + function transaction_option( $option_value ) { |
| 1254 | + //ooo, ugly. |
| 1255 | + if ( array_key_exists( $option_value, $this->transactions[$this->currentTransaction()] ) ) { |
| 1256 | + if ( $this->transactions[$this->currentTransaction()][$option_value] === true ) { |
| 1257 | + return true; |
| 1258 | + } |
| 1259 | + if ( is_array( $this->transactions[$this->currentTransaction()][$option_value] ) && |
| 1260 | + !empty( $this->transactions[$this->currentTransaction()][$option_value] ) ) { |
| 1261 | + return $this->transactions[$this->currentTransaction()][$option_value]; |
| 1262 | + } |
| 1263 | + } |
| 1264 | + return false; |
| 1265 | + } |
| 1266 | + |
| 1267 | +} |
Property changes on: trunk/extensions/DonationInterface/gateway_common/gateway.adapter.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 1268 | + native |
Index: trunk/extensions/DonationInterface/gateway_common/donation.api.php |
— | — | @@ -0,0 +1,164 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Generic Donation API |
| 5 | + * This API should be able to accept donation submissions for any gateway or payment type |
| 6 | + * Call with api.php?action=donate |
| 7 | + */ |
| 8 | +class DonationApi extends ApiBase { |
| 9 | + public function execute() { |
| 10 | + global $wgRequest, $wgParser; |
| 11 | + |
| 12 | + $params = $this->extractRequestParams(); |
| 13 | + $options = array(); |
| 14 | + |
| 15 | + $gateway = $params['gateway']; |
| 16 | + |
| 17 | + // If you want to test with fake data, pass a 'test' param set to true. |
| 18 | + // You still have to set the gateway you are testing though. |
| 19 | + // It looks like this only works if the Test global variable for that gateway is true. |
| 20 | + if ( array_key_exists( 'test', $params ) && $params['test'] ) { |
| 21 | + $params = $this->getTestData( $gateway ); |
| 22 | + $options['testData'] = $params; |
| 23 | + } |
| 24 | + |
| 25 | + $method = $params['payment_method']; |
| 26 | + |
| 27 | + if ( $gateway == 'payflowpro' ) { |
| 28 | + $gatewayObj = new PayflowProAdapter( $options ); |
| 29 | + switch ( $method ) { |
| 30 | + // TODO: add other payment methods |
| 31 | + default: |
| 32 | + $result = $gatewayObj->do_transaction( 'Card' ); |
| 33 | + } |
| 34 | + } else if ( $gateway == 'globalcollect' ) { |
| 35 | + $gatewayObj = new GlobalCollectAdapter( $options ); |
| 36 | + switch ( $method ) { |
| 37 | + // TODO: add other payment methods |
| 38 | + case 'card': |
| 39 | + $result = $gatewayObj->do_transaction( 'INSERT_ORDERWITHPAYMENT' ); |
| 40 | + break; |
| 41 | + default: |
| 42 | + $result = $gatewayObj->do_transaction( 'TEST_CONNECTION' ); |
| 43 | + } |
| 44 | + } else { |
| 45 | + $this->dieUsage( "Invalid gateway <<<$gateway>>> passed to Donation API.", 'unknown_gateway' ); |
| 46 | + } |
| 47 | + |
| 48 | + //$normalizedData = $gatewayObj->getData(); |
| 49 | + $outputResult = array(); |
| 50 | + $outputResult['message'] = $result['message']; |
| 51 | + $outputResult['status'] = $result['status']; |
| 52 | + $outputResult['returnurl'] = $result['data']['PAYMENT']['RETURNURL']; |
| 53 | + $outputResult['errors'] = implode( '; ', $result['errors'] ); |
| 54 | + |
| 55 | + $this->getResult()->addValue( 'data', 'request', $params ); |
| 56 | + $this->getResult()->addValue( 'data', 'result', $outputResult ); |
| 57 | + |
| 58 | + /* |
| 59 | + $this->getResult()->setIndexedTagName( $result, 'response' ); |
| 60 | + $this->getResult()->addValue( 'data', 'result', $result ); |
| 61 | + */ |
| 62 | + } |
| 63 | + |
| 64 | + public function getAllowedParams() { |
| 65 | + return array( |
| 66 | + 'gateway' => $this->defineParam( true ), |
| 67 | + 'test' => $this->defineParam( false ), |
| 68 | + 'amount' => $this->defineParam( false ), |
| 69 | + 'currency' => $this->defineParam( false ), |
| 70 | + 'fname' => $this->defineParam( false ), |
| 71 | + 'mname' => $this->defineParam( false ), |
| 72 | + 'lname' => $this->defineParam( false ), |
| 73 | + 'street' => $this->defineParam( false ), |
| 74 | + 'city' => $this->defineParam( false ), |
| 75 | + 'state' => $this->defineParam( false ), |
| 76 | + 'zip' => $this->defineParam( false ), |
| 77 | + 'emailAdd' => $this->defineParam( false ), |
| 78 | + 'country' => $this->defineParam( false ), |
| 79 | + 'card_num' => $this->defineParam( false ), |
| 80 | + 'card_type' => $this->defineParam( false ), |
| 81 | + 'expiration' => $this->defineParam( false ), |
| 82 | + 'cvv' => $this->defineParam( false ), |
| 83 | + 'payment_method' => $this->defineParam( false ), |
| 84 | + 'language' => $this->defineParam( false ), |
| 85 | + ); |
| 86 | + } |
| 87 | + |
| 88 | + private function defineParam( $required = false, $type = 'string' ) { |
| 89 | + if ( $required ) { |
| 90 | + $param = array( ApiBase::PARAM_TYPE => $type, ApiBase::PARAM_REQUIRED => true ); |
| 91 | + } else { |
| 92 | + $param = array( ApiBase::PARAM_TYPE => $type ); |
| 93 | + } |
| 94 | + return $param; |
| 95 | + } |
| 96 | + |
| 97 | + private function getTestData( $gateway ) { |
| 98 | + $params = array( |
| 99 | + 'gateway' => $gateway, |
| 100 | + 'amount' => "35", |
| 101 | + 'currency' => 'USD', |
| 102 | + 'fname' => 'Tester', |
| 103 | + 'mname' => 'T.', |
| 104 | + 'lname' => 'Testington', |
| 105 | + 'street' => '548 Market St.', |
| 106 | + 'city' => 'San Francisco', |
| 107 | + 'state' => 'CA', |
| 108 | + 'zip' => '94104', |
| 109 | + 'emailAdd' => 'test@example.com', |
| 110 | + 'country' => 'US', |
| 111 | + 'payment_method' => 'card', |
| 112 | + 'language' => 'en', |
| 113 | + 'card_type' => '1', // Is this valid for PayflowPro? |
| 114 | + ); |
| 115 | + if ( $gateway != 'globalcollect' ) { |
| 116 | + $params += array( |
| 117 | + 'card_num' => '378282246310005', |
| 118 | + 'expiration' => date( 'my', strtotime( '+1 year 1 month' ) ), |
| 119 | + 'cvv' => '001', |
| 120 | + ); |
| 121 | + } |
| 122 | + return $params; |
| 123 | + } |
| 124 | + |
| 125 | + public function getParamDescription() { |
| 126 | + return array( |
| 127 | + 'gateway' => 'Which payment gateway to use - payflowpro, globalcollect, etc.', |
| 128 | + 'test' => 'Set to true if you want to use bogus test data instead of supplying your own', |
| 129 | + 'amount' => 'The amount donated', |
| 130 | + 'currency' => 'Currency code', |
| 131 | + 'fname' => 'First name', |
| 132 | + 'mname' => 'Middle name', |
| 133 | + 'lname' => 'Last name', |
| 134 | + 'street' => 'First line of street address', |
| 135 | + 'city' => 'City', |
| 136 | + 'state' => 'State abbreviation', |
| 137 | + 'zip' => 'Postal code', |
| 138 | + 'emailAdd' => 'Email address', |
| 139 | + 'country' => 'Country code', |
| 140 | + 'card_num' => 'Credit card number', |
| 141 | + 'card_type' => 'Credit card type', |
| 142 | + 'expiration' => 'Expiration date', |
| 143 | + 'cvv' => 'CVV security code', |
| 144 | + 'payment_method' => 'Payment method to use', |
| 145 | + 'language' => 'Language code', |
| 146 | + ); |
| 147 | + } |
| 148 | + |
| 149 | + public function getDescription() { |
| 150 | + return array( |
| 151 | + 'This API allow you to submit a donation to the Wikimedia Foundation using a', |
| 152 | + 'variety of payment processors.', |
| 153 | + ); |
| 154 | + } |
| 155 | + |
| 156 | + public function getExamples() { |
| 157 | + return array( |
| 158 | + 'api.php?action=donate&gateway=payflowpro&amount=2.00¤cy=USD', |
| 159 | + ); |
| 160 | + } |
| 161 | + |
| 162 | + public function getVersion() { |
| 163 | + return __CLASS__ . ': $Id: DonationApi.php 1.0 kaldari $'; |
| 164 | + } |
| 165 | +} |
Index: trunk/extensions/DonationInterface/gateway_common/DonationData.php |
— | — | @@ -0,0 +1,718 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Description of DonationData |
| 6 | + * |
| 7 | + * @author khorn |
| 8 | + */ |
| 9 | +class DonationData { |
| 10 | + |
| 11 | + protected $normalized = array( ); |
| 12 | + public $boss; |
| 13 | + |
| 14 | + function __construct( $owning_class, $test = false, $data = false ) { |
| 15 | + //TODO: Actually think about this bit. |
| 16 | + // ...and keep in mind we can re-populate if it's a test or whatever. (But that may not be a good idea either) |
| 17 | + $this->boss = $owning_class; |
| 18 | + $this->gatewayID = $this->getGatewayIdentifier(); |
| 19 | + $this->populateData( $test, $data ); |
| 20 | + } |
| 21 | + |
| 22 | + function populateData( $test = false, $testdata = false ) { |
| 23 | + global $wgRequest; |
| 24 | + $this->normalized = array( ); |
| 25 | + if ( $test ) { |
| 26 | + $this->populateData_Test( $testdata ); |
| 27 | + } else { |
| 28 | + $this->normalized = array( |
| 29 | + 'posted' => 'true', //moderately sneaky. |
| 30 | + 'amount' => $wgRequest->getText( 'amount', null ), |
| 31 | + 'amountGiven' => $wgRequest->getText( 'amountGiven', null ), |
| 32 | + 'amountOther' => $wgRequest->getText( 'amountOther', null ), |
| 33 | + 'email' => $wgRequest->getText( 'emailAdd' ), |
| 34 | + 'fname' => $wgRequest->getText( 'fname' ), |
| 35 | + 'mname' => $wgRequest->getText( 'mname' ), |
| 36 | + 'lname' => $wgRequest->getText( 'lname' ), |
| 37 | + 'street' => $wgRequest->getText( 'street' ), |
| 38 | + 'city' => $wgRequest->getText( 'city' ), |
| 39 | + 'state' => $wgRequest->getText( 'state' ), |
| 40 | + 'zip' => $wgRequest->getText( 'zip' ), |
| 41 | + 'country' => $wgRequest->getText( 'country' ), |
| 42 | + 'fname2' => $wgRequest->getText( 'fname' ), |
| 43 | + 'lname2' => $wgRequest->getText( 'lname' ), |
| 44 | + 'street2' => $wgRequest->getText( 'street' ), |
| 45 | + 'city2' => $wgRequest->getText( 'city' ), |
| 46 | + 'state2' => $wgRequest->getText( 'state' ), |
| 47 | + 'zip2' => $wgRequest->getText( 'zip' ), |
| 48 | + /** |
| 49 | + * For legacy reasons, we might get a 0-length string passed into the form for country2. If this happens, we need to set country2 |
| 50 | + * to be 'country' for downstream processing (until we fully support passing in two separate addresses). I thought about completely |
| 51 | + * disabling country2 support in the forms, etc but realized there's a chance it'll be resurrected shortly. Hence this silly hack. |
| 52 | + */ |
| 53 | + 'country2' => ( strlen( $wgRequest->getText( 'country2' ) ) ) ? $wgRequest->getText( 'country2' ) : $wgRequest->getText( 'country' ), |
| 54 | + 'size' => $wgRequest->getText( 'size' ), |
| 55 | + 'premium_language' => $wgRequest->getText( 'premium_language', "en" ), |
| 56 | + 'card_num' => str_replace( ' ', '', $wgRequest->getText( 'card_num' ) ), |
| 57 | + 'card_type' => $wgRequest->getText( 'card_type' ), |
| 58 | + 'expiration' => $wgRequest->getText( 'mos' ) . substr( $wgRequest->getText( 'year' ), 2, 2 ), |
| 59 | + 'cvv' => $wgRequest->getText( 'cvv' ), |
| 60 | + 'currency' => $wgRequest->getText( 'currency_code' ), |
| 61 | + 'payment_method' => $wgRequest->getText( 'payment_method' ), |
| 62 | + 'order_id' => $wgRequest->getText( 'order_id', null ), //as far as I know, this won't actually ever pull anything back. |
| 63 | + 'i_order_id' => $wgRequest->getText( 'i_order_id', null ), //internal id for each contribution attempt |
| 64 | + 'numAttempt' => $wgRequest->getVal( 'numAttempt', 0 ), |
| 65 | + 'referrer' => ( $wgRequest->getVal( 'referrer' ) ) ? $wgRequest->getVal( 'referrer' ) : $wgRequest->getHeader( 'referer' ), |
| 66 | + 'utm_source' => self::getUtmSource(), //TODO: yes. That. |
| 67 | + 'utm_medium' => $wgRequest->getText( 'utm_medium' ), |
| 68 | + 'utm_campaign' => $wgRequest->getText( 'utm_campaign' ), |
| 69 | + // try to honor the user-set language (uselang), otherwise the language set in the URL (language) |
| 70 | + 'language' => $wgRequest->getText( 'uselang', $wgRequest->getText( 'language' ) ), |
| 71 | + 'comment-option' => $wgRequest->getText( 'comment-option' ), |
| 72 | + 'comment' => $wgRequest->getText( 'comment' ), |
| 73 | + 'email-opt' => $wgRequest->getText( 'email-opt' ), |
| 74 | + 'test_string' => $wgRequest->getText( 'process' ), // for showing payflow string during testing |
| 75 | + '_cache_' => $wgRequest->getText( '_cache_', null ), |
| 76 | + 'token' => $wgRequest->getText( 'token', null ), |
| 77 | + 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ), |
| 78 | + 'data_hash' => $wgRequest->getText( 'data_hash' ), |
| 79 | + 'action' => $wgRequest->getText( 'action' ), |
| 80 | + 'gateway' => $wgRequest->getText( 'gateway' ), //likely to be reset shortly by setGateway(); |
| 81 | + 'owa_session' => $wgRequest->getText( 'owa_session', null ), |
| 82 | + 'owa_ref' => $wgRequest->getText( 'owa_ref', null ), |
| 83 | + 'transaction_type' => $wgRequest->getText( 'transaction_type', null ), // Used by GlobalCollect for payment types |
| 84 | + ); |
| 85 | + if ( !$wgRequest->wasPosted() ) { |
| 86 | + $this->setVal( 'posted', false ); |
| 87 | + } |
| 88 | + } |
| 89 | + $this->doCacheStuff(); |
| 90 | + |
| 91 | + $this->normalizeAndSanitize(); |
| 92 | + |
| 93 | + //TODO: determine if _nocache_ is still a thing anywhere. |
| 94 | + if ( !empty( $this->normalized ) && ( $this->getVal( 'numAttempt' ) == '0' && ((!$this->getVal( 'utm_source_id' ) == false ) || $this->getVal( '_nocache_' ) == 'true' ) ) ) { |
| 95 | + $this->saveContributionTracking(); |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + function getData() { |
| 100 | + return $this->normalized; |
| 101 | + } |
| 102 | + |
| 103 | + function isCache() { |
| 104 | + return $this->cache; |
| 105 | + } |
| 106 | + |
| 107 | + function populateData_Test( $testdata = false ) { |
| 108 | + if ( is_array( $testdata ) ) { |
| 109 | + $this->normalized = $testdata; |
| 110 | + } else { |
| 111 | + // define arrays of cc's and cc #s for random selection |
| 112 | + $cards = array( 'american' ); |
| 113 | + $card_nums = array( |
| 114 | + 'american' => array( |
| 115 | + 378282246310005 |
| 116 | + ), |
| 117 | + ); |
| 118 | + |
| 119 | + // randomly select a credit card |
| 120 | + $card_index = array_rand( $cards ); |
| 121 | + |
| 122 | + // randomly select a credit card # |
| 123 | + $card_num_index = array_rand( $card_nums[$cards[$card_index]] ); |
| 124 | + |
| 125 | + global $wgRequest; //TODO: ARRRGHARGHARGH. That is all. |
| 126 | + |
| 127 | + $this->normalized = array( |
| 128 | + 'amount' => "35", |
| 129 | + 'amountOther' => '', |
| 130 | + 'email' => 'test@example.com', |
| 131 | + 'fname' => 'Tester', |
| 132 | + 'mname' => 'T.', |
| 133 | + 'lname' => 'Testington', |
| 134 | + 'street' => '548 Market St.', |
| 135 | + 'city' => 'San Francisco', |
| 136 | + 'state' => 'CA', |
| 137 | + 'zip' => '94104', |
| 138 | + 'country' => 'US', |
| 139 | + 'fname2' => 'Testy', |
| 140 | + 'lname2' => 'Testerson', |
| 141 | + 'street2' => '123 Telegraph Ave.', |
| 142 | + 'city2' => 'Berkeley', |
| 143 | + 'state2' => 'CA', |
| 144 | + 'zip2' => '94703', |
| 145 | + 'country2' => 'US', |
| 146 | + 'size' => 'small', |
| 147 | + 'premium_language' => 'es', |
| 148 | + 'card_num' => $card_nums[$cards[$card_index]][$card_num_index], |
| 149 | + 'card_type' => $cards[$card_index], |
| 150 | + 'expiration' => date( 'my', strtotime( '+1 year 1 month' ) ), |
| 151 | + 'cvv' => '001', |
| 152 | + 'currency' => 'USD', |
| 153 | + 'payment_method' => $wgRequest->getText( 'payment_method' ), |
| 154 | + 'order_id' => '1234567890', |
| 155 | + 'i_order_id' => '1234567890', |
| 156 | + 'numAttempt' => 0, |
| 157 | + 'referrer' => 'http://www.baz.test.com/index.php?action=foo&action=bar', |
| 158 | + 'utm_source' => self::getUtmSource(), |
| 159 | + 'utm_medium' => $wgRequest->getText( 'utm_medium' ), |
| 160 | + 'utm_campaign' => $wgRequest->getText( 'utm_campaign' ), |
| 161 | + 'language' => 'en', |
| 162 | + 'comment-option' => $wgRequest->getText( 'comment-option' ), |
| 163 | + 'comment' => $wgRequest->getText( 'comment' ), |
| 164 | + 'email-opt' => $wgRequest->getText( 'email-opt' ), |
| 165 | + 'test_string' => $wgRequest->getText( 'process' ), |
| 166 | + 'token' => '', |
| 167 | + 'contribution_tracking_id' => $wgRequest->getText( 'contribution_tracking_id' ), |
| 168 | + 'data_hash' => $wgRequest->getText( 'data_hash' ), |
| 169 | + 'action' => $wgRequest->getText( 'action' ), |
| 170 | + 'gateway' => 'payflowpro', |
| 171 | + 'owa_session' => $wgRequest->getText( 'owa_session', null ), |
| 172 | + 'owa_ref' => 'http://localhost/defaultTestData', |
| 173 | + 'transaction_type' => '', // Used by GlobalCollect for payment types |
| 174 | + ); |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + function isSomething( $key ) { |
| 179 | + if ( array_key_exists( $key, $this->normalized ) && !empty( $this->normalized[$key] ) ) { |
| 180 | + return true; |
| 181 | + } else { |
| 182 | + return false; |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + function getVal( $key ) { |
| 187 | + if ( $this->isSomething( $key ) ) { |
| 188 | + return $this->normalized[$key]; |
| 189 | + } else { |
| 190 | + return null; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + function setVal( $key, $val ) { |
| 195 | + $this->normalized[$key] = $val; |
| 196 | + } |
| 197 | + |
| 198 | + function expunge( $key ) { |
| 199 | + if ( array_key_exists( $key, $this->normalized ) ) { |
| 200 | + unset( $this->normalized[$key] ); |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + function normalizeAndSanitize() { |
| 205 | + if ( !empty( $this->normalized ) ) { |
| 206 | + $this->setNormalizedAmount(); |
| 207 | + $this->setNormalizedOrderIDs(); |
| 208 | + $this->setGateway(); |
| 209 | + $this->setNormalizedOptOuts(); |
| 210 | + array_walk( $this->normalized, array( $this, 'sanitizeInput' ) ); |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + function setNormalizedAmount() { |
| 215 | + if ( !($this->isSomething( 'amount' )) || !(preg_match( '/^\d+(\.(\d+)?)?$/', $this->getVal( 'amount' ) ) ) ) { |
| 216 | + if ( $this->isSomething( 'amountGiven' ) && preg_match( '/^\d+(\.(\d+)?)?$/', $this->getVal( 'amountGiven' ) ) ) { |
| 217 | + $this->setVal( 'amount', number_format( $this->getVal( 'amountGiven' ), 2, '.', '' ) ); |
| 218 | + } elseif ( $this->isSomething( 'amount' ) && $this->getVal( 'amount' ) == '-1' ) { |
| 219 | + $this->setVal( 'amount', $this->getVal( 'amountOther' ) ); |
| 220 | + } else { |
| 221 | + $this->setVal( 'amount', '0.00' ); |
| 222 | + } |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + function setOwaRefId() { |
| 227 | + //Our data should already be pulled and whatever. |
| 228 | + if ( $this->isSomething( 'owa_ref' ) && !is_numeric( $this->normalized['owa_ref'] ) ) { |
| 229 | + $owa_ref = $this->get_owa_ref_id( $owa_ref ); |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | + function setNormalizedOrderIDs() { |
| 234 | + //basically, we need a new order_id every time we come through here, but if there's an internal already there, |
| 235 | + //we want to use that one internally. So. |
| 236 | + //Exception: If we pass in an order ID in the querystring: Don't mess with it. |
| 237 | + //TODO: I'm pretty sure I'm not supposed to do this directly. |
| 238 | + if ( array_key_exists( 'order_id', $_GET ) ) { |
| 239 | + $this->setVal( 'order_id', $_GET['order_id'] ); |
| 240 | + $this->setVal( 'i_order_id', $_GET['order_id'] ); |
| 241 | + return; |
| 242 | + } |
| 243 | + |
| 244 | + $this->setVal( 'order_id', $this->generateOrderId() ); |
| 245 | + if ( !$this->isSomething( 'i_order_id' ) ) { |
| 246 | + $this->setVal( 'i_order_id', $this->generateOrderId() ); |
| 247 | + } |
| 248 | + } |
| 249 | + |
| 250 | + /** |
| 251 | + * Generate an order id exactly once for this go-round. |
| 252 | + */ |
| 253 | + function generateOrderId() { |
| 254 | + static $order_id = null; |
| 255 | + if ( $order_id === null ) { |
| 256 | + $order_id = ( double ) microtime() * 1000000 . mt_rand( 1000, 9999 ); |
| 257 | + } |
| 258 | + return $order_id; |
| 259 | + } |
| 260 | + |
| 261 | + /** |
| 262 | + * Sanitize user input |
| 263 | + * |
| 264 | + * Intended to be used with something like array_walk |
| 265 | + * |
| 266 | + * @param $value The value of the array |
| 267 | + * @param $key The key of the array |
| 268 | + * @param $flags The flag constant for htmlspecialchars |
| 269 | + * @param $double_encode Whether or not to double-encode strings |
| 270 | + */ |
| 271 | + public function sanitizeInput( &$value, $key, $flags=ENT_COMPAT, $double_encode=false ) { |
| 272 | + $value = htmlspecialchars( $value, $flags, 'UTF-8', $double_encode ); |
| 273 | + } |
| 274 | + |
| 275 | + function log( $message, $log_level=LOG_INFO ) { |
| 276 | + $c = $this->getAdapterClass(); |
| 277 | + if ( $c && is_callable( array( $c, 'log' ) )){ |
| 278 | + $c::log( $message, $log_level ); |
| 279 | + } |
| 280 | + } |
| 281 | + |
| 282 | + function getGatewayIdentifier() { |
| 283 | + $c = $this->getAdapterClass(); |
| 284 | + if ( $c && is_callable( array( $c, 'getIdentifier' ) ) ){ |
| 285 | + return $c::getIdentifier(); |
| 286 | + } else { |
| 287 | + return 'DonationData'; |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + function getGatewayGlobal( $varname ) { |
| 292 | + $c = $this->getAdapterClass(); |
| 293 | + if ( $c && is_callable( array( $c, 'getGlobal' ) ) ){ |
| 294 | + return $c::getGlobal( $varname ); |
| 295 | + } else { |
| 296 | + return false; |
| 297 | + } |
| 298 | + } |
| 299 | + |
| 300 | + function setGateway() { |
| 301 | + //TODO: Hum. If we have some other gateway in the form data, should we go crazy here? (Probably) |
| 302 | + $gateway = $this->gatewayID; |
| 303 | + $this->setVal( 'gateway', $gateway ); |
| 304 | + } |
| 305 | + |
| 306 | + function doCacheStuff() { |
| 307 | + //TODO: Wow, name. |
| 308 | + global $wgRequest; |
| 309 | + |
| 310 | + // if _cache_ is requested by the user, do not set a session/token; dynamic data will be loaded via ajax |
| 311 | + if ( $this->isSomething( '_cache_' ) ) { |
| 312 | + self::log( $this->getAnnoyingOrderIDLogLinePrefix() . ' Cache requested', LOG_DEBUG ); |
| 313 | + $this->cache = true; //TODO: If we don't need this, kill it in the face. |
| 314 | + $this->setVal( 'token', 'cache' ); |
| 315 | + |
| 316 | + // if we have squid caching enabled, set the maxage |
| 317 | + global $wgUseSquid, $wgOut; |
| 318 | + $maxAge = $this->getGatewayGlobal( 'SMaxAge' ); |
| 319 | + |
| 320 | + if ( $wgUseSquid && ( $maxAge !== false ) ) { |
| 321 | + self::log( $this->getAnnoyingOrderIDLogLinePrefix() . ' Setting s-max-age: ' . $maxAge, LOG_DEBUG ); |
| 322 | + $wgOut->setSquidMaxage( $maxAge ); |
| 323 | + } |
| 324 | + } else { |
| 325 | + $this->cache = false; //TODO: Kill this one in the face, too. (see above) |
| 326 | + } |
| 327 | + } |
| 328 | + |
| 329 | + function getAnnoyingOrderIDLogLinePrefix() { |
| 330 | + //TODO: ...aww. But it's so descriptive. |
| 331 | + return $this->getVal( 'order_id' ) . ' ' . $this->getVal( 'i_order_id' ) . ': '; |
| 332 | + } |
| 333 | + |
| 334 | + /** |
| 335 | + * Establish an 'edit' token to help prevent CSRF, etc |
| 336 | + * |
| 337 | + * We use this in place of $wgUser->editToken() b/c currently |
| 338 | + * $wgUser->editToken() is broken (apparently by design) for |
| 339 | + * anonymous users. Using $wgUser->editToken() currently exposes |
| 340 | + * a security risk for non-authenticated users. Until this is |
| 341 | + * resolved in $wgUser, we'll use our own methods for token |
| 342 | + * handling. |
| 343 | + * |
| 344 | + * @var mixed $salt |
| 345 | + * @return string |
| 346 | + */ |
| 347 | + public function getEditToken( $salt = '' ) { |
| 348 | + |
| 349 | + // make sure we have a session open for tracking a CSRF-prevention token |
| 350 | + self::ensureSession(); |
| 351 | + |
| 352 | + $gateway_ident = $this->gatewayID; |
| 353 | + |
| 354 | + if ( !isset( $_SESSION[$gateway_ident . 'EditToken'] ) ) { |
| 355 | + // generate unsalted token to place in the session |
| 356 | + $token = self::generateToken(); |
| 357 | + $_SESSION[$gateway_ident . 'EditToken'] = $token; |
| 358 | + } else { |
| 359 | + $token = $_SESSION[$gateway_ident . 'EditToken']; |
| 360 | + } |
| 361 | + |
| 362 | + if ( is_array( $salt ) ) { |
| 363 | + $salt = implode( "|", $salt ); |
| 364 | + } |
| 365 | + return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX; |
| 366 | + } |
| 367 | + |
| 368 | + /** |
| 369 | + * Generate a token string |
| 370 | + * |
| 371 | + * @var mixed $salt |
| 372 | + * @return string |
| 373 | + */ |
| 374 | + public static function generateToken( $salt = '' ) { |
| 375 | + $token = dechex( mt_rand() ) . dechex( mt_rand() ); |
| 376 | + return md5( $token . $salt ); |
| 377 | + } |
| 378 | + |
| 379 | + /** |
| 380 | + * Determine the validity of a token |
| 381 | + * |
| 382 | + * @var string $val |
| 383 | + * @var mixed $salt |
| 384 | + * @return bool |
| 385 | + */ |
| 386 | + function matchEditToken( $val, $salt = '' ) { |
| 387 | + // fetch a salted version of the session token |
| 388 | + $sessionToken = $this->getEditToken( $salt ); |
| 389 | + if ( $val != $sessionToken ) { |
| 390 | + wfDebug( "DonationData::matchEditToken: broken session data\n" ); |
| 391 | + } |
| 392 | + $this->log( "Val = $val" ); |
| 393 | + $this->log( "Session Token = $sessionToken" ); |
| 394 | + return $val == $sessionToken; |
| 395 | + } |
| 396 | + |
| 397 | + /** |
| 398 | + * Unset the payflow edit token from a user's session |
| 399 | + */ |
| 400 | + function unsetEditToken() { |
| 401 | + $gateway_ident = $this->gatewayID; |
| 402 | + |
| 403 | + if ( isset( $_SESSION ) && isset( $_SESSION[$gateway_ident . 'EditToken'] ) ){ |
| 404 | + unset( $_SESSION[$gateway_ident . 'EditToken'] ); |
| 405 | + } |
| 406 | + } |
| 407 | + |
| 408 | + /** |
| 409 | + * Ensure that we have a session set for the current user |
| 410 | + * |
| 411 | + * If we do not have a session set for the current user, |
| 412 | + * start the session. |
| 413 | + */ |
| 414 | + public static function ensureSession() { |
| 415 | + // if the session is already started, do nothing |
| 416 | + if ( session_id() ) |
| 417 | + return; |
| 418 | + |
| 419 | + // otherwise, fire it up using global mw function wfSetupSession |
| 420 | + wfSetupSession(); |
| 421 | + } |
| 422 | + |
| 423 | + public function checkTokens() { |
| 424 | + global $wgRequest; |
| 425 | + static $match = null; |
| 426 | + |
| 427 | + if ( $match === null ) { |
| 428 | + $salt = $this->getGatewayGlobal( 'Salt' ); |
| 429 | + if ( $salt === false ){ |
| 430 | + $salt = 'gotToBeInAUnitTest'; |
| 431 | + } |
| 432 | + |
| 433 | + // establish the edit token to prevent csrf |
| 434 | + $token = $this->getEditToken( $salt ); |
| 435 | + |
| 436 | + $this->log( $this->getAnnoyingOrderIDLogLinePrefix() . ' editToken: ' . $token, LOG_DEBUG ); |
| 437 | + |
| 438 | + // match token |
| 439 | + $token_check = ( $this->isSomething( 'token' ) ) ? $this->getVal( 'token' ) : $token; //TODO: does this suck as much as it looks like it does? |
| 440 | + $match = $this->matchEditToken( $token_check, $salt ); |
| 441 | + if ( $wgRequest->wasPosted() ) { |
| 442 | + $this->log( $this->getAnnoyingOrderIDLogLinePrefix() . ' Submitted edit token: ' . $this->getVal( 'token' ), LOG_DEBUG ); |
| 443 | + $this->log( $this->getAnnoyingOrderIDLogLinePrefix() . ' Token match: ' . ($match ? 'true' : 'false' ), LOG_DEBUG ); |
| 444 | + } |
| 445 | + } |
| 446 | + |
| 447 | + return $match; |
| 448 | + } |
| 449 | + |
| 450 | + /** |
| 451 | + * Get the utm_source string |
| 452 | + * |
| 453 | + * Checks to see if the utm_source is set properly for the credit card |
| 454 | + * form including any cc form variants (identified by utm_source_id). If |
| 455 | + * anything cc form related is out of place for the utm_source, this |
| 456 | + * will fix it. |
| 457 | + * |
| 458 | + * the utm_source is structured as: banner.landing_page.payment_instrument |
| 459 | + * |
| 460 | + * @param string $utm_source The utm_source for tracking - if not passed directly, |
| 461 | + * we try to figure it out from the request object |
| 462 | + * @param int $utm_source_id The utm_source_id for tracking - if not passed directly, |
| 463 | + * we try to figure it out from the request object |
| 464 | + * @return string The full utm_source |
| 465 | + */ |
| 466 | + public static function getUtmSource( $utm_source = null, $utm_source_id = null ) { |
| 467 | + global $wgRequest; |
| 468 | + |
| 469 | + /** |
| 470 | + * fetch whatever was passed in as the utm_source |
| 471 | + * |
| 472 | + * if utm_source was not passed in as a param, we try to divine it from |
| 473 | + * the request. if it's not set there, no big deal, we'll just be |
| 474 | + * missing some tracking data. |
| 475 | + */ |
| 476 | + if ( is_null( $utm_source ) ) { |
| 477 | + $utm_source = $wgRequest->getText( 'utm_source' ); |
| 478 | + } |
| 479 | + |
| 480 | + /** |
| 481 | + * if we have a utm_source_id, then the user is on a single-step credit card form. |
| 482 | + * if that's the case, we treat the single-step credit card form as a landing page, |
| 483 | + * which we label as cc#, where # = the utm_source_id |
| 484 | + */ |
| 485 | + if ( is_null( $utm_source_id ) ) { |
| 486 | + $utm_source_id = $wgRequest->getVal( 'utm_source_id', 0 ); |
| 487 | + } |
| 488 | + |
| 489 | + // this is how the CC portion of the utm_source should be defined |
| 490 | + $correct_cc_source = ( $utm_source_id ) ? 'cc' . $utm_source_id . '.cc' : 'cc'; |
| 491 | + |
| 492 | + // check to see if the utm_source is already correct - if so, return |
| 493 | + if ( preg_match( '/' . str_replace( ".", "\.", $correct_cc_source ) . '$/', $utm_source ) ) { |
| 494 | + return $utm_source; |
| 495 | + } |
| 496 | + |
| 497 | + // split the utm_source into its parts for easier manipulation |
| 498 | + $source_parts = explode( ".", $utm_source ); |
| 499 | + |
| 500 | + // if there are no sourceparts element, then the banner portion of the string needs to be set. |
| 501 | + // since we don't know what it is, set it to an empty string |
| 502 | + if ( !count( $source_parts ) ) |
| 503 | + $source_parts[0] = ''; |
| 504 | + |
| 505 | + // if the utm_source_id is set, set the landing page portion of the string to cc# |
| 506 | + $source_parts[1] = ( $utm_source_id ) ? 'cc' . $utm_source_id : ( isset( $source_parts[1] ) ? $source_parts[1] : '' ); |
| 507 | + |
| 508 | + // the payment instrument portion should always be 'cc' if this method is being accessed |
| 509 | + $source_parts[2] = 'cc'; |
| 510 | + |
| 511 | + // return a reconstructed string |
| 512 | + return implode( ".", $source_parts ); |
| 513 | + } |
| 514 | + |
| 515 | + /** |
| 516 | + * Determine proper opt-out settings for contribution tracking |
| 517 | + * |
| 518 | + * because the form elements for comment anonymization and email opt-out |
| 519 | + * are backwards (they are really opt-in) relative to contribution_tracking |
| 520 | + * (which is opt-out), we need to reverse the values. |
| 521 | + * NOTE: If you prune here, and there is a paypal redirect, you will have |
| 522 | + * problems with the email-opt/optout and comment-option/anonymous. |
| 523 | + */ |
| 524 | + function setNormalizedOptOuts( $prune = false ) { |
| 525 | + $optout['optout'] = ( $this->isSomething( 'email-opt' ) && $this->getVal( 'email-opt' ) == "1" ) ? '0' : '1'; |
| 526 | + $optout['anonymous'] = ( $this->isSomething( 'comment-option' ) && $this->getVal( 'comment-option' ) == "1" ) ? '0' : '1'; |
| 527 | + foreach ( $optout as $thing => $stuff ) { |
| 528 | + $this->setVal( $thing, $stuff ); |
| 529 | + } |
| 530 | + if ( $prune ) { |
| 531 | + $this->expunge( 'email-opt' ); |
| 532 | + $this->expunge( 'comment-option' ); |
| 533 | + } |
| 534 | + } |
| 535 | + |
| 536 | + /** |
| 537 | + * Clean array of tracking data to contain valid fields |
| 538 | + * |
| 539 | + * Compares tracking data array to list of valid tracking fields and |
| 540 | + * removes any extra tracking fields/data. Also sets empty values to |
| 541 | + * 'null' values. |
| 542 | + * @param bool $clean_opouts |
| 543 | + */ |
| 544 | + public function getCleanTrackingData() { |
| 545 | + |
| 546 | + // define valid tracking fields |
| 547 | + $tracking_fields = array( |
| 548 | + 'note', |
| 549 | + 'referrer', |
| 550 | + 'anonymous', |
| 551 | + 'utm_source', |
| 552 | + 'utm_medium', |
| 553 | + 'utm_campaign', |
| 554 | + 'optout', |
| 555 | + 'language', |
| 556 | + 'ts' |
| 557 | + ); |
| 558 | + |
| 559 | + foreach ( $tracking_fields as $value ) { |
| 560 | + if ( $this->isSomething( $value ) ) { |
| 561 | + $tracking_data[$value] = $this->getVal( $value ); |
| 562 | + } else { |
| 563 | + $tracking_data[$value] = null; |
| 564 | + } |
| 565 | + } |
| 566 | + |
| 567 | + return $tracking_data; |
| 568 | + } |
| 569 | + |
| 570 | + //if ( !empty($data) && ( $data[ 'numAttempt' ] == '0' && ( !$wgRequest->getText( 'utm_source_id', false ) || $wgRequest->getText( '_nocache_' ) == 'true' ) ) ) { |
| 571 | + //so, basically, if this is the first attempt. This seems to get called nowhere else. |
| 572 | + function saveContributionTracking() { |
| 573 | + |
| 574 | + $tracked_contribution = $this->getCleanTrackingData(); |
| 575 | + |
| 576 | + // insert tracking data and get the tracking id |
| 577 | + $result = self::insertContributionTracking( $tracked_contribution ); |
| 578 | + |
| 579 | + $this->setVal( 'contribution_tracking_id', $result ); |
| 580 | + |
| 581 | + if ( !$result ) { |
| 582 | + return false; |
| 583 | + } |
| 584 | + return true; |
| 585 | + } |
| 586 | + |
| 587 | + /** |
| 588 | + * Insert a record into the contribution_tracking table |
| 589 | + * |
| 590 | + * @param array $tracking_data The array of tracking data to insert to contribution_tracking |
| 591 | + * @return mixed Contribution tracking ID or false on failure |
| 592 | + */ |
| 593 | + public static function insertContributionTracking( $tracking_data ) { |
| 594 | + $db = ContributionTrackingProcessor::contributionTrackingConnection(); |
| 595 | + |
| 596 | + if ( !$db ) { |
| 597 | + return false; |
| 598 | + } |
| 599 | + |
| 600 | + // set the time stamp if it's not already set |
| 601 | + if ( !isset( $tracking_data['ts'] ) || !strlen( $tracking_data['ts'] ) ) { |
| 602 | + $tracking_data['ts'] = $db->timestamp(); |
| 603 | + } |
| 604 | + |
| 605 | + // Store the contribution data |
| 606 | + if ( $db->insert( 'contribution_tracking', $tracking_data ) ) { |
| 607 | + return $db->insertId(); |
| 608 | + } else { |
| 609 | + return false; |
| 610 | + } |
| 611 | + } |
| 612 | + |
| 613 | + /** |
| 614 | + * Update contribution_tracking table |
| 615 | + * |
| 616 | + * @param array $data Form data |
| 617 | + * @param bool $force If set to true, will ensure that contribution tracking is updated |
| 618 | + */ |
| 619 | + //Looks like two places: Either right before a paypal redirect (if that's still a thing) or |
| 620 | + //just prior to curling something up to some server somewhere. |
| 621 | + //I took care of the one just prior to curling. |
| 622 | + public function updateContributionTracking( $force = false ) { |
| 623 | + // ony update contrib tracking if we're coming from a single-step landing page |
| 624 | + // which we know with cc# in utm_source or if force=true or if contribution_tracking_id is not set |
| 625 | + if ( !$force && |
| 626 | + !preg_match( "/cc[0-9]/", $this->getVal( 'utm_source' ) ) && |
| 627 | + is_numeric( $this->getVal( 'contribution_tracking_id' ) ) ) { |
| 628 | + return; |
| 629 | + } |
| 630 | + |
| 631 | + $db = ContributionTrackingProcessor::contributionTrackingConnection(); |
| 632 | + |
| 633 | + if ( !$db ) { |
| 634 | + return true; |
| 635 | + } ///wait, what? TODO: This line was straight copied from the _gateway.body. Find out if there's a good reason we're not returning false here. |
| 636 | + |
| 637 | + $tracked_contribution = $this->getCleanTrackingData(); |
| 638 | + |
| 639 | + // if contrib tracking id is not already set, we need to insert the data, otherwise update |
| 640 | + if ( !$this->getVal( 'contribution_tracking_id' ) ) { |
| 641 | + $this->setVal( 'contribution_tracking_id', $this->insertContributionTracking( $tracked_contribution ) ); |
| 642 | + } else { |
| 643 | + $db->update( 'contribution_tracking', $tracked_contribution, array( 'id' => $this->getVal( 'contribution_tracking_id' ) ) ); |
| 644 | + } |
| 645 | + } |
| 646 | + |
| 647 | + public function addDonorDataToSession() { |
| 648 | + self::ensureSession(); |
| 649 | + $donordata = array( |
| 650 | + 'email', |
| 651 | + 'fname', |
| 652 | + 'mname', |
| 653 | + 'lname', |
| 654 | + 'street', |
| 655 | + 'city', |
| 656 | + 'state', |
| 657 | + 'zip', |
| 658 | + 'country', |
| 659 | + 'contribution_tracking_id', |
| 660 | + 'referrer' |
| 661 | + ); |
| 662 | + |
| 663 | + foreach ( $donordata as $item ) { |
| 664 | + if ( $this->isSomething( $item ) ) { |
| 665 | + $_SESSION['Donor'][$item] = $this->getVal( $item ); |
| 666 | + } |
| 667 | + } |
| 668 | + } |
| 669 | + |
| 670 | + public function populateDonorFromSession() { |
| 671 | + if ( array_key_exists( 'Donor', $_SESSION ) ) { |
| 672 | + $this->addData( $_SESSION['Donor'] ); |
| 673 | + } |
| 674 | + } |
| 675 | + |
| 676 | + /** |
| 677 | + * TODO: Consider putting all the session data for a gateway under something like |
| 678 | + * $_SESSION[$gateway_identifier] |
| 679 | + * so we can kill it all with one stroke. |
| 680 | + */ |
| 681 | + public function unsetAllDDSessionData() { |
| 682 | + unset( $_SESSION['Donor'] ); |
| 683 | + $this->unsetEditToken(); |
| 684 | + } |
| 685 | + |
| 686 | + public function addData( $newdata ) { |
| 687 | + if ( is_array( $newdata ) && !empty( $newdata ) ) { |
| 688 | + foreach ( $newdata as $key => $val ) { |
| 689 | + if ( !is_array( $val ) ) { |
| 690 | + $this->setVal( $key, $val ); |
| 691 | + } |
| 692 | + } |
| 693 | + } |
| 694 | + $this->normalizeAndSanitize(); |
| 695 | + } |
| 696 | + |
| 697 | + public function incrementNumAttempt() { |
| 698 | + if ( $this->isSomething( 'numAttempt' ) ) { |
| 699 | + $attempts = $this->getVal( 'numAttempt' ); |
| 700 | + if ( is_numeric( $attempts ) ) { |
| 701 | + $this->setVal( 'numAttempt', $attempts + 1 ); |
| 702 | + } else { |
| 703 | + //assume garbage = 0, so... |
| 704 | + $this->setVal( 'numAttempt', 1 ); |
| 705 | + } |
| 706 | + } |
| 707 | + } |
| 708 | + |
| 709 | + function getAdapterClass(){ |
| 710 | + if ( class_exists( $this->boss ) ) { |
| 711 | + return $this->boss; |
| 712 | + } else { |
| 713 | + return false; |
| 714 | + } |
| 715 | + } |
| 716 | + |
| 717 | +} |
| 718 | + |
| 719 | +?> |
Property changes on: trunk/extensions/DonationInterface/gateway_common/DonationData.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 720 | + native |
Index: trunk/extensions/DonationInterface/gateway_common/GatewayForm.php |
— | — | @@ -0,0 +1,542 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class GatewayForm extends UnlistedSpecialPage { |
| 5 | + |
| 6 | + /** |
| 7 | + * Defines the action to take on a transaction. |
| 8 | + * |
| 9 | + * Possible values include 'process', 'challenge', |
| 10 | + * 'review', 'reject'. These values can be set during |
| 11 | + * data processing validation, for instance. |
| 12 | + * |
| 13 | + * Hooks are exposed to handle the different actions. |
| 14 | + * |
| 15 | + * Defaults to 'process'. |
| 16 | + * @var string |
| 17 | + */ |
| 18 | + public $action = 'process'; |
| 19 | + |
| 20 | + /** |
| 21 | + * A container for the form class |
| 22 | + * |
| 23 | + * Used to loard the form object to display the CC form |
| 24 | + * @var object |
| 25 | + */ |
| 26 | + public $form_class; |
| 27 | + |
| 28 | + /** |
| 29 | + * An array of form errors |
| 30 | + * @var array |
| 31 | + */ |
| 32 | + public $errors = array( ); |
| 33 | + public $adapter; //the adapter goes here. |
| 34 | + |
| 35 | + /** |
| 36 | + * The form is assumed to be successful. Errors in the form must set this to |
| 37 | + * false. |
| 38 | + * |
| 39 | + * @var boolean |
| 40 | + */ |
| 41 | + public $validateFormResult = true; |
| 42 | + |
| 43 | + function __construct() { |
| 44 | + $me = get_called_class(); |
| 45 | + parent::__construct( $me ); |
| 46 | + $this->errors = $this->getPossibleErrors(); |
| 47 | + $this->setFormClass(); |
| 48 | + } |
| 49 | + |
| 50 | + /** |
| 51 | + * Checks posted form data for errors and returns array of messages |
| 52 | + * |
| 53 | + * This is update to GatewayForm::fnValidateForm(). |
| 54 | + * |
| 55 | + * @param array $data Reference to the data of the form |
| 56 | + * @param array $error Reference to the error messages of the form |
| 57 | + * @param array $options |
| 58 | + * OPTIONAL - You may require certain field groups to be validated |
| 59 | + * - address - Validates: street, city, state, zip |
| 60 | + * - amount - Validates: amount |
| 61 | + * - creditCard - Validates: card_num, cvv, expiration and sets the card |
| 62 | + * - email - Validates: email |
| 63 | + * - name - Validates: fname, lname |
| 64 | + * |
| 65 | + * @return 0|1 Returns 0 on success and 1 on failure |
| 66 | + * |
| 67 | + * @see GatewayForm::fnValidateForm() |
| 68 | + */ |
| 69 | + public function validateForm( &$data, &$error, $options = array( ) ) { |
| 70 | + |
| 71 | + extract( $options ); |
| 72 | + |
| 73 | + // Set which items will be validated |
| 74 | + $address = isset( $address ) ? ( boolean ) $address : true; |
| 75 | + $amount = isset( $amount ) ? ( boolean ) $amount : true; |
| 76 | + $creditCard = isset( $creditCard ) ? ( boolean ) $creditCard : false; |
| 77 | + $email = isset( $email ) ? ( boolean ) $email : true; |
| 78 | + $name = isset( $name ) ? ( boolean ) $name : true; |
| 79 | + |
| 80 | + // These are set in the order they will most likely appear on the form. |
| 81 | + |
| 82 | + if ( $name ) { |
| 83 | + $this->validateName( $data, $error ); |
| 84 | + } |
| 85 | + |
| 86 | + if ( $address ) { |
| 87 | + $this->validateAddress( $data, $error ); |
| 88 | + } |
| 89 | + |
| 90 | + if ( $amount ) { |
| 91 | + $this->validateAmount( $data, $error ); |
| 92 | + } |
| 93 | + |
| 94 | + if ( $email ) { |
| 95 | + $this->validateEmail( $data, $error ); |
| 96 | + } |
| 97 | + |
| 98 | + if ( $creditCard ) { |
| 99 | + $this->validateCreditCard( $data, $error ); |
| 100 | + } |
| 101 | + |
| 102 | + /* |
| 103 | + * $error_result would return 0 on success, 1 on failure. |
| 104 | + * |
| 105 | + * This is done for backward compatibility. |
| 106 | + */ |
| 107 | + return $this->getValidateFormResult() ? 0 : 1; |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + * Validates the address |
| 112 | + * |
| 113 | + * @param array $data Reference to the data of the form |
| 114 | + * @param array $error Reference to the error messages of the form |
| 115 | + * |
| 116 | + * @see GatewayForm::validateForm() |
| 117 | + */ |
| 118 | + public function validateAddress( &$data, &$error ) { |
| 119 | + |
| 120 | + if ( empty( $data['street'] ) ) { |
| 121 | + |
| 122 | + $error['street'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-street' ); |
| 123 | + |
| 124 | + $this->setValidateFormResult( false ); |
| 125 | + } |
| 126 | + |
| 127 | + if ( empty( $data['city'] ) ) { |
| 128 | + |
| 129 | + $error['city'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-city' ); |
| 130 | + |
| 131 | + $this->setValidateFormResult( false ); |
| 132 | + } |
| 133 | + |
| 134 | + if ( empty( $data['state'] ) ) { |
| 135 | + |
| 136 | + $error['state'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-state' ); |
| 137 | + |
| 138 | + $this->setValidateFormResult( false ); |
| 139 | + } |
| 140 | + |
| 141 | + if ( empty( $data['zip'] ) && $data['state'] != 'XX') { |
| 142 | + |
| 143 | + $error['zip'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-zip' ); |
| 144 | + |
| 145 | + $this->setValidateFormResult( false ); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Validates the amount contributed |
| 151 | + * |
| 152 | + * @param array $data Reference to the data of the form |
| 153 | + * @param array $error Reference to the error messages of the form |
| 154 | + * |
| 155 | + * @see GatewayForm::validateForm() |
| 156 | + */ |
| 157 | + public function validateAmount( &$data, &$error ) { |
| 158 | + |
| 159 | + if ( empty( $data['amount'] ) ) { |
| 160 | + |
| 161 | + $error['amount'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-amount' ); |
| 162 | + |
| 163 | + $this->setValidateFormResult( false ); |
| 164 | + } |
| 165 | + |
| 166 | + // check amount |
| 167 | + $priceFloor = $this->adapter->getGlobal( 'PriceFloor' ); |
| 168 | + $priceCeiling = $this->adapter->getGlobal( 'PriceCeiling' ); |
| 169 | + if ( !preg_match( '/^\d+(\.(\d+)?)?$/', $data['amount'] ) || |
| 170 | + ( ( float ) $this->convert_to_usd( $data['currency'], $data['amount'] ) < ( float ) $priceFloor || |
| 171 | + ( float ) $this->convert_to_usd( $data['currency'], $data['amount'] ) > ( float ) $priceCeiling ) ) { |
| 172 | + |
| 173 | + $error['invalidamount'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-invalid-amount' ); |
| 174 | + |
| 175 | + $this->setValidateFormResult( false ); |
| 176 | + } |
| 177 | + } |
| 178 | + |
| 179 | + /** |
| 180 | + * Validates a credit card |
| 181 | + * |
| 182 | + * @param array $data Reference to the data of the form |
| 183 | + * @param array $error Reference to the error messages of the form |
| 184 | + * |
| 185 | + * @see GatewayForm::validateForm() |
| 186 | + */ |
| 187 | + public function validateCreditCard( &$data, &$error ) { |
| 188 | + |
| 189 | + if ( empty( $data['card_num'] ) ) { |
| 190 | + |
| 191 | + $error['card_num'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-card_num' ); |
| 192 | + |
| 193 | + $this->setValidateFormResult( false ); |
| 194 | + } |
| 195 | + |
| 196 | + if ( empty( $data['cvv'] ) ) { |
| 197 | + |
| 198 | + $error['cvv'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-cvv' ); |
| 199 | + |
| 200 | + $this->setValidateFormResult( false ); |
| 201 | + } |
| 202 | + |
| 203 | + if ( empty( $data['expiration'] ) ) { |
| 204 | + |
| 205 | + $error['expiration'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-expiration' ); |
| 206 | + |
| 207 | + $this->setValidateFormResult( false ); |
| 208 | + } |
| 209 | + |
| 210 | + // validate that credit card number entered is correct and set the card type |
| 211 | + if ( preg_match( '/^3[47][0-9]{13}$/', $data['card_num'] ) ) { // american express |
| 212 | + $data['card'] = 'american'; |
| 213 | + } elseif ( preg_match( '/^5[1-5][0-9]{14}$/', $data['card_num'] ) ) { // mastercard |
| 214 | + $data['card'] = 'mastercard'; |
| 215 | + } elseif ( preg_match( '/^4[0-9]{12}(?:[0-9]{3})?$/', $data['card_num'] ) ) {// visa |
| 216 | + $data['card'] = 'visa'; |
| 217 | + } elseif ( preg_match( '/^6(?:011|5[0-9]{2})[0-9]{12}$/', $data['card_num'] ) ) { // discover |
| 218 | + $data['card'] = 'discover'; |
| 219 | + } else { // an invalid credit card number was entered |
| 220 | + $error['card_num'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-card-num' ); |
| 221 | + |
| 222 | + $this->setValidateFormResult( false ); |
| 223 | + } |
| 224 | + } |
| 225 | + |
| 226 | + /** |
| 227 | + * Validates an email address. |
| 228 | + * |
| 229 | + * @param array $data Reference to the data of the form |
| 230 | + * @param array $error Reference to the error messages of the form |
| 231 | + * |
| 232 | + * @see GatewayForm::validateForm() |
| 233 | + */ |
| 234 | + public function validateEmail( &$data, &$error ) { |
| 235 | + |
| 236 | + if ( empty( $data['email'] ) ) { |
| 237 | + |
| 238 | + $error['email'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-email-empty' ); |
| 239 | + |
| 240 | + $this->setValidateFormResult( false ); |
| 241 | + } |
| 242 | + |
| 243 | + // is email address valid? |
| 244 | + $isEmail = User::isValidEmailAddr( $data['email'] ); |
| 245 | + |
| 246 | + // create error message (supercedes empty field message) |
| 247 | + if ( !$isEmail ) { |
| 248 | + $error['email'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-email' ); |
| 249 | + |
| 250 | + $this->setValidateFormResult( false ); |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + /** |
| 255 | + * Validates the name |
| 256 | + * |
| 257 | + * @param array $data Reference to the data of the form |
| 258 | + * @param array $error Reference to the error messages of the form |
| 259 | + * |
| 260 | + * @see GatewayForm::validateForm() |
| 261 | + */ |
| 262 | + public function validateName( &$data, &$error ) { |
| 263 | + |
| 264 | + if ( empty( $data['fname'] ) ) { |
| 265 | + |
| 266 | + $error['fname'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-fname' ); |
| 267 | + |
| 268 | + $this->setValidateFormResult( false ); |
| 269 | + } |
| 270 | + |
| 271 | + if ( empty( $data['lname'] ) ) { |
| 272 | + |
| 273 | + $error['lname'] = wfMsg( $this->adapter->getIdentifier() . '_gateway-error-msg-lname' ); |
| 274 | + |
| 275 | + $this->setValidateFormResult( false ); |
| 276 | + } |
| 277 | + } |
| 278 | + |
| 279 | + /** |
| 280 | + * Build and display form to user |
| 281 | + * |
| 282 | + * @param $data Array: array of posted user input |
| 283 | + * @param $error Array: array of error messages returned by validate_form function |
| 284 | + * |
| 285 | + * The message at the top of the form can be edited in the payflow_gateway.i18n.php file |
| 286 | + */ |
| 287 | + public function displayForm( &$data, &$error ) { |
| 288 | + global $wgOut, $wgRequest; |
| 289 | + |
| 290 | + $form_class = $this->getFormClass(); |
| 291 | + $form_obj = new $form_class( $data, $error, $this->adapter ); |
| 292 | + $form = $form_obj->getForm(); |
| 293 | + $wgOut->addHTML( $form ); |
| 294 | + } |
| 295 | + |
| 296 | + /** |
| 297 | + * Set the form class to use to generate the CC form |
| 298 | + * |
| 299 | + * @param string $class_name The class name of the form to use |
| 300 | + */ |
| 301 | + public function setFormClass( $class_name = NULL ) { |
| 302 | + if ( !$class_name ) { |
| 303 | + global $wgRequest; |
| 304 | + $defaultForm = $this->adapter->getGlobal( 'DefaultForm' ); |
| 305 | + $form_class = $wgRequest->getText( 'form_name', $defaultForm ); |
| 306 | + |
| 307 | + // make sure our form class exists before going on, if not try loading default form class |
| 308 | + $class_name = "Gateway_Form_" . $form_class; |
| 309 | + if ( !class_exists( $class_name ) ) { |
| 310 | + $class_name_orig = $class_name; |
| 311 | + $class_name = "Gateway_Form_" . $defaultForm; |
| 312 | + if ( !class_exists( $class_name ) ) { |
| 313 | + throw new MWException( 'Could not load form ' . $class_name_orig . ' nor default form ' . $class_name ); |
| 314 | + } |
| 315 | + } |
| 316 | + } |
| 317 | + $this->form_class = $class_name; |
| 318 | + |
| 319 | + //this should... maybe replace the other thing? I need it in the adapter so reCaptcha can get to it. |
| 320 | + $this->adapter->setFormClass( $class_name ); |
| 321 | + } |
| 322 | + |
| 323 | + /** |
| 324 | + * Get the currently set form class |
| 325 | + * |
| 326 | + * Will set the form class if the form class not already set |
| 327 | + * Using logic in setFormClass() |
| 328 | + * @return string |
| 329 | + */ |
| 330 | + public function getFormClass() { |
| 331 | + if ( !isset( $this->form_class ) ) { |
| 332 | + $this->setFormClass(); |
| 333 | + } |
| 334 | + return $this->form_class; |
| 335 | + } |
| 336 | + |
| 337 | + function displayResultsForDebug( $results ) { |
| 338 | + global $wgOut; |
| 339 | + if ( $this->adapter->getGlobal( 'DisplayDebug' ) !== true ){ |
| 340 | + return; |
| 341 | + } |
| 342 | + $wgOut->addHTML( HTML::element( 'span', null, $results['message'] ) ); |
| 343 | + |
| 344 | + if ( !empty( $results['errors'] ) ) { |
| 345 | + $wgOut->addHTML( "<ul>" ); |
| 346 | + foreach ( $results['errors'] as $code => $value ) { |
| 347 | + $wgOut->addHTML( HTML::element('li', null, "Error $code: $value" ) ); |
| 348 | + } |
| 349 | + $wgOut->addHTML( "</ul>" ); |
| 350 | + } |
| 351 | + |
| 352 | + if ( !empty( $results['data'] ) ) { |
| 353 | + $wgOut->addHTML( "<ul>" ); |
| 354 | + foreach ( $results['data'] as $key => $value ) { |
| 355 | + if ( is_array( $value ) ) { |
| 356 | + $wgOut->addHTML( HTML::element('li', null, $key ) . '<ul>' ); |
| 357 | + foreach ( $value as $key2 => $val2 ) { |
| 358 | + $wgOut->addHTML( HTML::element('li', null, "$key2: $val2" ) ); |
| 359 | + } |
| 360 | + $wgOut->addHTML( "</ul>" ); |
| 361 | + } else { |
| 362 | + $wgOut->addHTML( HTML::element('li', null, "$key: $value" ) ); |
| 363 | + } |
| 364 | + } |
| 365 | + $wgOut->addHTML( "</ul>" ); |
| 366 | + } else { |
| 367 | + $wgOut->addHTML( "Empty Results" ); |
| 368 | + } |
| 369 | + if ( array_key_exists( 'Donor', $_SESSION ) ) { |
| 370 | + $wgOut->addHTML( "Session Donor Vars:<ul>" ); |
| 371 | + foreach ( $_SESSION['Donor'] as $key => $val ) { |
| 372 | + $wgOut->addHTML( HTML::element('li', null, "$key: $val" ) ); |
| 373 | + } |
| 374 | + $wgOut->addHTML( "</ul>" ); |
| 375 | + } else { |
| 376 | + $wgOut->addHTML( "No Session Donor Vars:<ul>" ); |
| 377 | + } |
| 378 | + |
| 379 | + if ( is_array( $this->adapter->debugarray ) ) { |
| 380 | + $wgOut->addHTML( "Debug Array:<ul>" ); |
| 381 | + foreach ( $this->adapter->debugarray as $val ) { |
| 382 | + $wgOut->addHTML( HTML::element('li', null, $val ) ); |
| 383 | + } |
| 384 | + $wgOut->addHTML( "</ul>" ); |
| 385 | + } else { |
| 386 | + $wgOut->addHTML( "No Debug Array<ul>" ); |
| 387 | + } |
| 388 | + } |
| 389 | + |
| 390 | + public function getPossibleErrors() { |
| 391 | + return array( |
| 392 | + 'general' => '', |
| 393 | + 'retryMsg' => '', |
| 394 | + 'invalidamount' => '', |
| 395 | + 'card_num' => '', |
| 396 | + 'card_type' => '', |
| 397 | + 'cvv' => '', |
| 398 | + 'fname' => '', |
| 399 | + 'lname' => '', |
| 400 | + 'city' => '', |
| 401 | + 'country' => '', |
| 402 | + 'street' => '', |
| 403 | + 'state' => '', |
| 404 | + 'zip' => '', |
| 405 | + 'emailAdd' => '', |
| 406 | + ); |
| 407 | + } |
| 408 | + |
| 409 | + /** |
| 410 | + * Convert an amount for a particular currency to an amount in USD |
| 411 | + * |
| 412 | + * This is grosley rudimentary and likely wildly inaccurate. |
| 413 | + * This mimicks the hard-coded values used by the WMF to convert currencies |
| 414 | + * for validatoin on the front-end on the first step landing pages of their |
| 415 | + * donation process - the idea being that we can get a close approximation |
| 416 | + * of converted currencies to ensure that contributors are not going above |
| 417 | + * or below the price ceiling/floor, even if they are using a non-US currency. |
| 418 | + * |
| 419 | + * In reality, this probably ought to use some sort of webservice to get real-time |
| 420 | + * conversion rates. |
| 421 | + * |
| 422 | + * @param $currency_code |
| 423 | + * @param $amount |
| 424 | + * @return unknown_type |
| 425 | + */ |
| 426 | + public function convert_to_usd( $currency_code, $amount ) { |
| 427 | + switch ( strtoupper( $currency_code ) ) { |
| 428 | + case 'USD': |
| 429 | + $usd_amount = $amount / 1; |
| 430 | + break; |
| 431 | + case 'GBP': |
| 432 | + $usd_amount = $amount / 1; |
| 433 | + break; |
| 434 | + case 'EUR': |
| 435 | + $usd_amount = $amount / 1; |
| 436 | + break; |
| 437 | + case 'AUD': |
| 438 | + $usd_amount = $amount / 2; |
| 439 | + break; |
| 440 | + case 'CAD': |
| 441 | + $usd_amount = $amount / 1; |
| 442 | + break; |
| 443 | + case 'CHF': |
| 444 | + $usd_amount = $amount / 1; |
| 445 | + break; |
| 446 | + case 'CZK': |
| 447 | + $usd_amount = $amount / 20; |
| 448 | + break; |
| 449 | + case 'DKK': |
| 450 | + $usd_amount = $amount / 5; |
| 451 | + break; |
| 452 | + case 'HKD': |
| 453 | + $usd_amount = $amount / 10; |
| 454 | + break; |
| 455 | + case 'HUF': |
| 456 | + $usd_amount = $amount / 200; |
| 457 | + break; |
| 458 | + case 'JPY': |
| 459 | + $usd_amount = $amount / 100; |
| 460 | + break; |
| 461 | + case 'NZD': |
| 462 | + $usd_amount = $amount / 2; |
| 463 | + break; |
| 464 | + case 'NOK': |
| 465 | + $usd_amount = $amount / 10; |
| 466 | + break; |
| 467 | + case 'PLN': |
| 468 | + $usd_amount = $amount / 5; |
| 469 | + break; |
| 470 | + case 'SGD': |
| 471 | + $usd_amount = $amount / 2; |
| 472 | + break; |
| 473 | + case 'SEK': |
| 474 | + $usd_amount = $amount / 10; |
| 475 | + break; |
| 476 | + case 'ILS': |
| 477 | + $usd_amount = $amount / 5; |
| 478 | + break; |
| 479 | + default: |
| 480 | + $usd_amount = $amount; |
| 481 | + break; |
| 482 | + } |
| 483 | + |
| 484 | + return $usd_amount; |
| 485 | + } |
| 486 | + |
| 487 | + public function log( $msg, $log_level=LOG_INFO ) { |
| 488 | + $this->adapter->log( $msg, $log_level ); |
| 489 | + } |
| 490 | + |
| 491 | + /** |
| 492 | + * Handle redirection of form content to PayPal |
| 493 | + * |
| 494 | + * @fixme If we can update contrib tracking table in ContributionTracking |
| 495 | + * extension, we can probably get rid of this method and just submit the form |
| 496 | + * directly to the paypal URL, and have all processing handled by ContributionTracking |
| 497 | + * This would make this a lot less hack-ish |
| 498 | + */ |
| 499 | + public function paypalRedirect() { |
| 500 | + global $wgOut; |
| 501 | + |
| 502 | + // if we don't have a URL enabled throw a graceful error to the user |
| 503 | + if ( !strlen( $this->adapter->getGlobal( 'PaypalURL' ) ) ) { |
| 504 | + $gateway_identifier = $this->adapter->getIdentifier(); |
| 505 | + $this->errors['general']['nopaypal'] = wfMsg( $gateway_identifier . '_gateway-error-msg-nopaypal' ); |
| 506 | + return; |
| 507 | + } |
| 508 | + // submit the data to the paypal redirect URL |
| 509 | + $wgOut->redirect( $this->adapter->getPaypalRedirectURL() ); |
| 510 | + } |
| 511 | + |
| 512 | + /** |
| 513 | + * Fetch the array of iso country codes => country names |
| 514 | + * @return array |
| 515 | + */ |
| 516 | + public static function getCountries() { |
| 517 | + require_once( dirname( __FILE__ ) . '/../gateway_forms/includes/countryCodes.inc' ); |
| 518 | + return countryCodes(); |
| 519 | + } |
| 520 | + |
| 521 | + /** |
| 522 | + * Get validate form result |
| 523 | + * |
| 524 | + * @return boolean |
| 525 | + */ |
| 526 | + public function getValidateFormResult() { |
| 527 | + |
| 528 | + return ( boolean ) $this->validateFormResult; |
| 529 | + } |
| 530 | + |
| 531 | + /** |
| 532 | + * Set validate form result |
| 533 | + * |
| 534 | + * @param boolean $validateFormResult |
| 535 | + */ |
| 536 | + public function setValidateFormResult( $validateFormResult ) { |
| 537 | + |
| 538 | + $this->validateFormResult = empty( $validateFormResult ) ? false : ( boolean ) $validateFormResult; |
| 539 | + } |
| 540 | + |
| 541 | +} |
| 542 | + |
| 543 | +//end of GatewayForm class definiton |
Property changes on: trunk/extensions/DonationInterface/gateway_common/GatewayForm.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 544 | + native |