ColdBox - SagePay VSP Driect Plugin

Now I am getting more to grips with CB I have recreated an ecommerce site using the framework. I have also created a simple plugin for the Coldbox Framework that allows you to integrate SagePay VSP Direct payment system in your website. It seems to work very well the site I used it on has been running the new plugin for few days, taking transactions with no errors.

In case you don't know, SagePay VSP Direct is a method of processing a transaction through the Sage Pay gateway using server to server communication with the customer remaining on your website throughout the whole process.

This plugin will setup, send and handle the response from their gateway. You will need an understanding of Sagepay and a SSL cert to use this plugin. Sage Pay only support UK merchants at the moment!

I will be adding some example of the handler, checkout forms, and cart to a zip download soon at the moment all I have included the plugin CFC code below.

Continue Reading

Jun21

Sage Pay CFC

Below is a quick update on the old 'protx.cfc' now 'sagepay.cfc'. It's a little messy and there are a few things I would have done different if I had more time, but I didn't. I wanted to share will you my new CFC because as you may know Sage Pay don't seem to have a ColdFusion VSP Direct integration Kit anymore! Anyhow below will help jump start anyone wanting to integration the gatway using the VSP Direct service they offer.

First make sure you have read their integration guides @ sagepay.com and you will need have a test or simulation account to test first. If you don't have one you can sign up for a free simulation account here. You can get a live account by clicking here.

Download the sagepay.cfc (.rar file 3KB)

Continue Reading

May28

New Protx (Sage Pay) VSP Direct URL's

If you are using my CFC for Protx VSP Direct Integration (now sage pay) you will need to change the live and testing URL's. SagePay told us that the old Live URL's would still work, however the way they are just forwarding in the system means they don't work correctly with CFHTTP calls. The replacement code with the new URL's is below.

view plain print about
1<cffunction name="SetGateway" access="public" hint="set which gateway is to be used, simulator, test or live" returntype="struct">
2 <!--- Set up local scope. --->
3 <cfset GatewaySettings = StructNew() />
4 <cfif #SimulatorSite# is "1" >
5 <cfscript>
6 StructInsert(GatewaySettings, "Verify", "false");
7 StructInsert(GatewaySettings, "PurchaseURL", "https://test.sagepay.com/Simulator/VSPDirectGateway.asp");
8
StructInsert(GatewaySettings, "
RefundURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorRefundTx");
9
StructInsert(GatewaySettings, "ReleaseURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorReleaseTx");
10
StructInsert(GatewaySettings, "
RepeatURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorRepeatTx");
11
StructInsert(GatewaySettings, "callbackURL", "https://test.sagepay.com/Simulator/VSPDirectCallback.asp");
12
</cfscript>
13 </cfif>
14 <cfif #TestSite# is "
1">
15 <cfscript>

16 StructInsert(GatewaySettings, "
Verify", "false");
17 StructInsert(GatewaySettings, "
PurchaseURL", "https://test.sagepay.com/gateway/service/vspdirect-register.vsp");
18
StructInsert(GatewaySettings, "RefundURL", "https://test.sagepay.com/gateway/service/refund.vsp");
19
StructInsert(GatewaySettings, "
ReleaseURL", "https://test.sagepay.com/gateway/service/release.vsp");
20
StructInsert(GatewaySettings, "RepeatURL", "https://test.sagepay.com/gateway/service/repeat.vsp");
21
StructInsert(GatewaySettings, "
callbackURL", "https://test.sagepay.com/gateway/service/direct3dcallback.vsp");
22
</cfscript>
23 <!---https://ukvpstest.protx.com/showpost/showpost.asp--->
24 </cfif>
25 <cfif #LiveSite# is "1">
26 <cfscript>
27 StructInsert(GatewaySettings, "Verify", "false");
28 StructInsert(GatewaySettings, "PurchaseURL", "https://live.sagepay.com/gateway/service/vspdirect-register.vsp");
29
StructInsert(GatewaySettings, "
RefundURL", "https://live.sagepay.com/gateway/service/refund.vsp");
30
StructInsert(GatewaySettings, "ReleaseURL", "https://live.sagepay.com/gateway/service/release.vsp");
31
StructInsert(GatewaySettings, "
RepeatURL", "https://live.sagepay.com/gateway/service/repeat.vsp");
32
StructInsert(GatewaySettings, "callbackURL", "https://live.sagepay.com/gateway/service/direct3dcallback.vsp");
33
</cfscript>
34 </cfif>
35 <cfreturn GatewaySettings>
36</cffunction>

Apr22

Protx rebrands now it's Sage Pay

Protx, the UK's leading payment gateway provider as you may well be aware was purchased by Sage last year. Well another change, as of the 21st of April Protx will be rebranded as 'Sage Pay'. Since the buyout I must comment that Sage Pay (Protx) support and their services have improved significantly.

As we are Sage Pay partners we will still be offering the same services as before and customers who use our services will not have to do a thing! Your API and SalesMaxx Cart will be updated for you. For anyone who is not with us or is a developer remember to change your logos. No news yet about the VSP Direct URL's, I assume they will still work and they will let us know if they change them also.

Hopefully this change means in the future we could have closer integration with Sage itself, although you can now use Sage Pay within Sage, the other way, in as always has been a little more tricky.

Apr16

IIS's compression scheme incompatible with CFHTTP

Today some of our sites that make CFHTTP calls to Protx's VSP Direct Server stopped working! The CFHTTP response I was getting back was "Connection Failure: Status code unavailable".

A quick search one the Net and I came across this post: Workaround for CFHTTP and Compressed HTTP Response from IIS.

Basically it's to do with the HTTP Compression in IIS. CFHTTP couldn't read the compressed header. This is due to IIS's compression scheme being incompatible with CFHTTP, so I have read.

The quick fix was to add the following CFHTTPPARAM to the CFHTTP post so the header is sent back uncompressed.

view plain print about
1<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
2<cfhttpparam type="Header" name="TE" value="deflate;q=0">

This resolved the problem, however this issue according to some other blog posts I read was resolved in MX7 Cumulative Hot Fix back in 9/10/2007, however I am running CF 8.01 maybe this needs addressing again?

Dec05

ColdFusion and Protx VSP Direct

Protx no longer have examples of how to integrate ColdFusion with their VSP Direct Payment Gateway. Protx are one of the UK's biggest gateway providers and one would expect an integration kit in ColdFusion. They have told me they have no plans to add one now or in the near future. The old kit was ok but had no examples of the new 3D Secure system, and because it was way out date they took it off the site.

Basically here is a reverse engineered version I did from there ASP.NET example which very works well. I know its a little messy at the mo, but it will give anyone who knowns CF and Protx a good starting template. The idea is when i have time to create a custom tag.

If you want to have a go yourself here is the guide to work from: VSP Guide

My example already assumes you have created the payment details page with all the necessary form fields and have a understanding of Protx. The code also has some of my cart vars in which you would need to create or replace with your own.

First create a component called gateway-protx.cfc

view plain print about
1<cfcomponent>
2<cfscript>
3SimulatorSite ="0";
4TestSite ="0";
5LiveSite ="1";
6
</cfscript>
7
8<cffunction name="SetGateway" access="public" hint="set which gateway is to be used, simulator, test or live" returntype="struct">
9 <!--- Set up local scope. --->
10 <cfset GatewaySettings = StructNew() />
11 <cfif #SimulatorSite# is "1" >
12 <cfscript>
13 StructInsert(GatewaySettings, "Verify", "false");
14 StructInsert(GatewaySettings, "PurchaseURL", "https://ukvpstest.protx.com/VSPSimulator/VSPDirectGateway.asp");
15
StructInsert(GatewaySettings, "
RefundURL", "https://ukvpstest.protx.com/VSPSimulator/VSPServerGateway.asp?Service=VendorRefundTx");
16
StructInsert(GatewaySettings, "ReleaseURL", "https://ukvpstest.protx.com/VSPSimulator/VSPServerGateway.asp?Service=VendorReleaseTx");
17
StructInsert(GatewaySettings, "
RepeatURL", "https://ukvpstest.protx.com/VSPSimulator/VSPServerGateway.asp?Service=VendorRepeatTx");
18
StructInsert(GatewaySettings, "callbackURL", "https://ukvpstest.protx.com/VSPSimulator/VSPDirectCallback.asp");
19
</cfscript>
20 </cfif>
21 <cfif #TestSite# is "
1">
22 <cfscript>

23 StructInsert(GatewaySettings, "
Verify", "false");
24 StructInsert(GatewaySettings, "
PurchaseURL", "https://ukvpstest.protx.com/vspgateway/service/vspdirect-register.vsp");
25
StructInsert(GatewaySettings, "RefundURL", "https://ukvpstest.protx.com/vspgateway/service/refund.vsp");
26
StructInsert(GatewaySettings, "
ReleaseURL", "https://ukvpstest.protx.com/vspgateway/service/release.vsp");
27
StructInsert(GatewaySettings, "RepeatURL", "https://ukvpstest.protx.com/vspgateway/service/repeat.vsp");
28
StructInsert(GatewaySettings, "
callbackURL", "https://ukvpstest.protx.com/vspgateway/service/direct3dcallback.vsp");
29
</cfscript>
30 <!---https://ukvpstest.protx.com/showpost/showpost.asp--->
31 </cfif>
32 <cfif #LiveSite# is "1">
33 <cfscript>
34 StructInsert(GatewaySettings, "Verify", "false");
35 StructInsert(GatewaySettings, "PurchaseURL", "https://ukvps.protx.com/vspgateway/service/vspdirect-register.vsp");
36
StructInsert(GatewaySettings, "
RefundURL", "https://ukvps.protx.com/vspgateway/service/refund.vsp");
37
StructInsert(GatewaySettings, "ReleaseURL", "https://ukvps.protx.com/vspgateway/service/release.vsp");
38
StructInsert(GatewaySettings, "
RepeatURL", "https://ukvps.protx.com/vspgateway/service/repeat.vsp");
39
StructInsert(GatewaySettings, "callbackURL", "https://ukvps.protx.com/vspgateway/service/direct3dcallback.vsp");
40
</cfscript>
41 </cfif>
42 <cfreturn GatewaySettings>
43</cffunction>
44
45<!---
46'****************************************************************************************
47' Protx HTTP Call
48'****************************************************************************************
49--->

50<cffunction name="
gatewaySend" access="public" hint="checkes form dedtails" returntype="struct">
51 <cfargument name="
PurchaseURL" type="string" required="yes">
52 <cfargument name="
ProtocolVersion" type="string" required="yes">
53 <cfargument name="
Vendor" type="string" required="yes">
54 <cfargument name="
VendorTxCode" type="string" required="yes">
55 <cfargument name="
DefaultCurrency" type="string" required="yes">
56 <cfargument name="
DefaultDescription" type="string" required="yes">
57 <cfargument name="
DefaultApplyAVSCV2" type="string" required="yes">
58
59
60 <cfargument name="
DeliveryAddress" type="string" required="yes">
61 <cfargument name="
DeliveryPostCode" type="string" required="yes">
62
63 <cfargument name="
CardNumber" type="string" required="yes">
64 <cfargument name="
CardHolder" type="string" required="yes">
65 <cfargument name="
Amount" type="string" required="yes">
66 <cfargument name="
StartDate" type="string" required="no">
67 <cfargument name="
ExpiryDate" type="string" required="yes">
68 <cfargument name="
Basket" type="string" required="no">
69
70 <cfargument name="
CustomerName" type="string" required="yes">
71 <cfargument name="
BillingAddress" type="string" required="yes">
72 <cfargument name="
BillingPostCode" type="string" required="yes">
73 <cfargument name="
ContactNumber" type="string" required="yes">
74 <cfargument name="
ContactFax" type="string" required="no">
75 <cfargument name="
CustomerEmail" type="string" required="yes">
76
77 <cfargument name="
ClientIPAddress" type="string" required="yes">
78    
79
80 <cfargument name="
XID" type="string" required="no">
81 <cfargument name="
ECI" type="string" required="no">
82 <cfargument name="
DSecureStatus" type="string" required="no">
83 <cfargument name="
CV2" type="string" required="no">
84 <cfargument name="
CAVV" type="string" required="no">
85
86 <cfargument name="
IssueNumber" type="string" required="no">
87
88
89
90 <!---Get the contents of the post from the previous page and split out the variables for sending--->
91 <cfset RequestData = GetHttpRequestData()>

92 <cfset Response = StructNew()>

93 <cfloop list="
#RequestData.content#" index="line" delimiters="&">
94 <cfset line = Trim( line )>

95 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), URLDecode(Trim(Mid(line,Find("=",line)+1,len(line)) )) )>
96 </cfloop>
97
98
99 <!---Set the required outgoing properties for the initial HTTPS post to the VPS--->
100 <!---******************HERE IS WHERE THE ORDER GETS SENT TO PROTX VIA HTTPS*********************** --->
101 <cfhttp url="#PurchaseURL#" method="post" delimiter="," resolveurl="no" throwonerror="yes" timeout="20" charset="windows-1252">
102 <cfhttpparam name="
VPSProtocol" value="#ProtocolVersion#" type="formfield">
103 <cfhttpparam name="
TxType" value="#Response.TxType#" type="formfield">
104 <cfhttpparam name="
Vendor" value="#Vendor#" type="formfield">
105 <cfhttpparam name="
VendorTxCode" value="#VendorTxCode#" type="formfield">
106 <cfhttpparam name="
Currency" value="#DefaultCurrency#" type="formfield">
107 <cfhttpparam name="
Description" value="#DefaultDescription#" type="formfield">
108 <cfhttpparam name="
Amount" value="#Amount#" type="formfield">
109 <cfhttpparam name="
CardHolder" value="#CardHolder#" type="formfield">
110 <cfhttpparam name="
CardNumber" value="#CardNumber#" type="formfield">
111 <cfhttpparam name="
GiftAidPayment" value="0" type="formfield">
112 <cfhttpparam name="
ApplyAVSCV2" value="#DefaultApplyAVSCV2#" type="formfield">
113
114 <cfhttpparam name="
Basket" value="#Basket#" type="formfield">
115
116    <cfif #StartDate# is not "
">
117 <cfhttpparam name="
StartDate" value="#StartDate#" type="formfield">
118 </cfif>
119 <cfif #ExpiryDate# is not "
">
120 <cfhttpparam name="
ExpiryDate" value="#ExpiryDate#" type="formfield">
121 </cfif>
122
123 <cfif #DeliveryAddress# is not "
">
124 <cfhttpparam name="
DeliveryAddress" value="#DeliveryAddress#" type="formfield">
125 </cfif>
126 <cfhttpparam name="
BillingAddress" value="#BillingAddress#" type="formfield">
127
128 <cfif #IssueNumber# is not "
">
129 <cfhttpparam name="
IssueNumber" value="#IssueNumber#" type="formfield">
130 </cfif>
131 <cfhttpparam name="
CV2" value="#CV2#" type="formfield">
132
133 <cfhttpparam name="
CardType" value="#CardType#" type="formfield">
134
135 <cfhttpparam name="
BillingPostCode" value="#BillingPostCode#" type="formfield">
136
137 <cfif #DeliveryPostCode# is not "
">
138 <cfhttpparam name="
DeliveryPostCode" value="#DeliveryPostCode#" type="formfield">
139 </cfif>
140
141 <cfhttpparam name="
CustomerName" value="#CustomerName#" type="formfield">
142
143    <cfif #ContactNumber# is not "
">
144 <cfhttpparam name="
ContactNumber" value="#ContactNumber#" type="formfield">
145 </cfif>
146 <cfif #ContactFax# is not "
">
147 <cfhttpparam name="
ContactFax" value="#ContactFax#" type="formfield">
148 </cfif>
149
150 <cfhttpparam name="
CustomerEmail" value="#CustomerEmail#" type="formfield">
151
152 <cfif #ClientIPAddress# is not "
">
153 <cfhttpparam name="
ClientIPAddress" value="#ClientIPAddress#" type="formfield">
154 </cfif>
155
156
157 <cfif #CAVV# is not "
">
158 <cfhttpparam name="
CAVV" value="#CAVV#" type="formfield">
159 </cfif>
160 <cfif #XID# is not "
">
161 <cfhttpparam name="
XID" value="#XID#" type="formfield">
162 </cfif>
163 <cfif #ECI# is not "
">
164 <cfhttpparam name="
ECI" value="#ECI#" type="formfield">
165 </cfif>
166 <cfif #DSecureStatus# is not "
">
167 <cfhttpparam name="
3DSecureStatus" value="#DSecureStatus#" type="formfield">
168 </cfif>
169
170
171 </cfhttp>
172
173
174 <!--- ********************************END OF HTTPS POST TO PROTX******************************************--->
175
176
177 <cfset Response = StructNew()>

178 <cfloop list="#CFHTTP.FileContent#" index="line" delimiters="#chr(13)#">
179 <cfset line = Trim( line )>

180 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), Trim(Mid(line,Find("=",line)+1,len(line)) ) )>
181 </cfloop>
182 <cfreturn Response>
183</cffunction>
184</cfcomponent>
Next I create a page called seek-auth.cfm this is the page you post all you form variables off to, its this page that does all the magic.
view plain print about
1<!---check you cart session to see if its empty--->
2<cfif structisempty(session['cartItem'])>
3 <cflocation addtoken="no" url="fielderror.cfm?ErrorCode=003" />
4 <cfabort>
5</cfif>
6
7<!---
8 '****************************************************************************************
9 ' validate all your form fields here
10 '****************************************************************************************
11 --->

12
13
14
15
16<!---
17 '****************************************************************************************
18 ' Protx Payment Gateway
19 '****************************************************************************************
20 --->

21<!---
22 '****************************************************************************************
23 ' Create cfc
24 '****************************************************************************************
25 --->

26<cfscript>
27 cfcProtx = createobject("component", "components.gateway-protx");
28 gatewaysetup = cfcProtx.SetGateway();
29
</cfscript>
30<!---
31 '****************************************************************************************
32 ' Protx Create Transaction ID -
33    This needs to be a random so this is what i use but could be made more secure
34 '****************************************************************************************
35 --->

36<cfif IsDefined("FORM.Amount") >
37 <cfset VendorTxCode = "#randrange(0,999999999)##randrange(0,999999999)#">
38</cfif>
39<!---
40 '****************************************************************************************
41 ' Setup new session to store any values we may need for post back etc
42 '****************************************************************************************
43 --->

44<cfif IsDefined("FORM.Amount") >
45 <cflock timeout="60" type="exclusive">
46 <cfset SESSION.protx = structNew()>
47 <cfset SESSION.protx.TxCode = #VendorTxCode#>
48 </cflock>
49</cfif>
50
51
52
53<!---
54 '****************************************************************************************
55 ' HTTPS Request to Protx - Uses SETUP PAGE Vars, and Forms Vars
56        ' PS these are all the form vars need to make a Protx Post
57 '****************************************************************************************
58 --->

59<cfif IsDefined("FORM.Amount")><!--- we only want this to run then we have a post from checkout--->
60 <cfparam name="CustomerName" default="#form.billname#">
61 <cfparam name="BillingAddress" default="#form.billaddress1# #form.billaddress1# #form.billaddress3# #form.billCountry#">
62 <cfparam name="BillingPostCode" default="#form.billPostCode#">
63 <cfparam name="ContactNumber" default="#form.billTel#">
64 <cfparam name="ContactFax" default="#form.billFax#">
65 <cfparam name="CustomerEmail" default="#form.email#">
66
67 <cfset keyList = structkeylist(session['cartItem'])>
68 <cfset subtotal = 0 >
69 <cfloop from="1" to="#listlen(keyList)#" index="y">
70 <cfset currentListItemDsp = session['cartItem'][listgetat(keyList,y)]>
71 <cfset itemName = currentListItemDsp['aName']>
72 <cfset itemQty = currentListItemDsp['aQty']>
73 <cfset itemPrice = currentListItemDsp['aPrice']>
74 <cfset totalOutput = itemPrice * itemQty>
75 <cfset subtotal = subtotal + totalOutput>
76
77 </cfloop>
78 <cfparam name="Basket" default="">
79 <cfparam name="DeliveryAddress" default="#form.shipaddress1# #form.shipaddress1# #form.shipaddress3# #form.shipCountry#">
80 <cfparam name="DeliveryPostCode" default="#form.shipPostCode#">
81 <cfset form.CardNumber=rereplace(form.CardNumber,"([^[0-9]]*)","","all")>
82 <cfparam name="CardNumber" default="#form.CardNumber#">
83 <cfparam name="CardHolder" default="#form.CardHolder#">
84 <cfparam name="Amount" default="#form.Amount#">
85 <cfparam name="StartDate" default="#form.StartDate1##form.StartDate2#">
86 <cfparam name="ExpiryDate" default="#form.ExpiryDate1##form.ExpiryDate2#">
87 <cfparam name="ClientIPAddress" default="#CGI.REMOTE_HOST#">
88 <cfparam name="CAVV" default="">
89 <cfparam name="XID" default="">
90 <cfparam name="ECI" default="">
91 <cfparam name="DSecureStatus" default="">
92 <cfparam name="CV2" default="">
93 <cfparam name="IssueNumber" default="#form.issueNumber#">
94
95<!---
96 '****************************************************************************************
97 ' Any database store here
98        ' If you need to store any info in your database do it here
99        ' Its also a good idea to set a 'waiting for authentication' in a file or db here
100 '****************************************************************************************
101 --->

102
103
104
105<!---if statment ends here--->
106</cfif>
107
108
109
110
111<!---
112 '****************************************************************************************
113 ' 3D Secure - New responce to sort out with 3D Secure info in it.
114 '****************************************************************************************
115 --->

116<cfif isDefined('PARes')>
117 <!---re check 3d post back--->
118 <cfhttp url="#callbackURL#" method="post" delimiter="," resolveurl="no" throwonerror="yes" timeout="20" charset="windows-1252">
119 <cfhttpparam name="MD" value="#FORM.MD#" type="formfield">
120 <cfhttpparam name="PARes" value="#FORM.PARes#" type="formfield">
121 </cfhttp>
122 <cfset Response = StructNew()>
123 <cfloop list="#CFHTTP.FileContent#" index="line" delimiters="#chr(13)#">
124 <cfset line = Trim( line )>
125 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), Trim(Mid(line,Find("=",line)+1,len(line)) ) )>
126 </cfloop>
127</cfif>
128
129<!---
130 '****************************************************************************************
131 ' HTTPS Responce- Determine next action if we have a responce
132 '****************************************************************************************
133 --->
<!---<cfdump var=#response#><cfabort>--->
134<cfif isdefined('Response')>
135 <!---Payment OK and Charged--->
136 <cfif #Response.Status# IS "OK" OR #Response.Status# IS "ATTEMPTONLY" >
137 <!---update order db here--->
138
139 <!---send user to the tank you page--->
140 <cflocation addtoken="no" url="thank-you.cfm?txcode=#SESSION.protx.TxCode#">
141
142 <!---Card is on the 3D Secure System --->
143 <cfelseif #Response.Status# IS "3DAUTH">
144 <h1>3D Secure Verification Needed, Loading please wait...</h1>
145 <InvalidTag LANGUAGE="Javascript">
146 function OnLoadEvent() { document.form.submit(); }
147 </SCRIPT>
148 <body OnLoad="OnLoadEvent();">
149 <cfoutput>
150 <form name="form" action="#Response.ACSURL#" method="POST"/>
151 <input type="hidden" name="PaReq" value="#Response.PaReq#"/>
152 <input type="hidden" name="TermUrl" value="https://#CGI.HTTP_HOST#/seek-auth.cfm"/>
153 <input type="hidden" name="MD" value="#Response.MD#"/>
154 </cfoutput>
155 <NOSCRIPT>
156 <center>
157 <p>Please click button below to Authenticate your card</p>
158 <input type="submit" value="Go"/>
159 </p>
160 </center>
161 </NOSCRIPT>
162 </form>
163 </body>
164 <!---The transaction returned failed!!!!!--->
165 <cfelse>
166 <cflock scope="session" timeout="15">
167 <cfset SESSION.protx.details = #Response.Status# & "<br />"& #Response.StatusDetail# >

168 </cflock>
169
170 <!---Update database--->    
171
172
173 <!---clean up vars --->
174
175 <cflocation addtoken="no" url="auth-error.cfm">
176 </cfif>
177</cfif><!---END of if responce is defined--->

Sep29