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.
2 hint="SagePay plugin for VSP Direct"
3 extends="coldbox.system.plugin"
4 output="false"
5 cache="true">
6<!------------------------------------------- CONSTRUCTOR ------------------------------------------->
7<cffunction name="init" access="public" returntype="any" output="false">
8 <cfargument name="controller" type="any" required="false" />
9 <cfscript>
10 super.Init(arguments.controller);
11 setpluginName("SagePay");
12 setpluginVersion("0.01");
13 setpluginDescription("SagePay plugin for VSP Direct using protocol version: 2.23");
14 //Set which environment to use, simulator,test or live
15 setEnvironment(gateway=getSetting("gatewayEnvironment"));
16 //Return instance
17 return this;
18 </cfscript>
19</cffunction>
20<!------------------------------------------- PUBLIC ------------------------------------------->
21<cffunction name="getPluginDetails" output="false" access="public" returntype="string" hint="Just for information and to test my first plugin function">
22 <!--- Get Event Context --->
23 <cfset var event = controller.getRequestService().getContext()>
24 <!--- Return the name in the event context --->
25 <cfreturn "#getpluginDescription()#. Have a nice day.">
26</cffunction>
27<!--- ************************************************************* --->
28<cffunction name="makeCall" access="public" hint="Make Call" returntype="struct">
29<!--- ************************************************************* --->
30<!---<cfargument name="PurchaseURL" type="string" required="yes">--->
31<cfargument name="VPSProtocol" type="string" required="yes">
32<cfargument name="vendor" type="string" required="yes">
33<cfargument name="VendorTxCode" type="string" required="yes">
34<cfargument name="DefaultCurrency" type="string" default="GBP" required="no">
35<cfargument name="Amount" type="any" required="yes">
36<cfargument name="CardHolder" type="string" required="yes">
37<cfargument name="CardNumber" type="string" required="yes">
38<cfargument name="DefaultApplyAVSCV2" type="string" required="no" default="0">
39<cfargument name="Basket" type="string" required="no" default="">
40<cfargument name="StartDate" type="string" required="no" default="">
41<cfargument name="ExpiryDate" type="string" required="yes">
42<cfargument name="DeliveryAddress" type="string" required="yes" default="">
43<cfargument name="CardType" type="string" required="yes">
44<cfargument name="BillingPostCode" type="string" required="yes">
45<cfargument name="DeliveryPostCode" type="string" required="yes">
46<cfargument name="CustomerName" type="string" required="yes">
47<cfargument name="ContactNumber" type="string" required="no" default="">
48<cfargument name="ContactFax" type="string" required="no" default="">
49<cfargument name="CustomerEmail" type="string" required="no" default="">
50<cfargument name="ClientIPAddress" type="string" required="no" default="#CGI.REMOTE_ADDR#">
51<cfargument name="CAVV" type="string" required="no" default="">
52<cfargument name="XID" type="string" required="no" default="">
53<cfargument name="ECI" type="string" required="no" default="">
54<cfargument name="DSecureStatus" type="string" required="no" default="">
55<cfargument name="CV2" type="string" required="no">
56<cfargument name="referrerID" type="string" required="no" default="">
57<cfargument name="DefaultDescription" type="string" required="no" default="Payment From #CustomerName#">
58<cfargument name="billfirstName" type="string" required="yes">
59<cfargument name="billlastName" type="string" required="yes">
60<cfargument name="BillingAddress1" type="string" required="yes">
61<cfargument name="billingcity" type="string" required="yes">
62<cfargument name="BillingCountry" type="string" required="no" default="GB">
63<cfargument name="ISSUENUMBER" type="string" required="no" default="">
64<!--- ************************************************************* --->
65<!--- ************************************************************* --->
66 <!---Get the contents of the post from the previous page and split out the variables for sending--->
67 <cfset RequestData = GetHttpRequestData()>
68 <cfset Response = StructNew()>
69 <cfloop list="#RequestData.content#" index="line" delimiters="&">
70 <cfset line = Trim( line )>
71 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), URLDecode(Trim(Mid(line,Find("=",line)+1,len(line)) )) )>
72 </cfloop>
73<!--- ************************************************************* --->
74<!---Set the required outgoing properties for the initial HTTPS post to the VPS--->
75<!--- ************************************************************* --->
76 <!---******************HERE IS WHERE THE ORDER GETS SENT TO PROTX VIA HTTPS*********************** --->
77 <cfhttp url="#GatewaySettings.PurchaseURL#" method="post" delimiter="," throwonerror="no">
78 <!---to combat IIS's compression scheme incompatible with CFHTTP this issue was fixed in MX7 but is back in CF8--->
79 <cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
80 <cfhttpparam type="Header" name="TE" value="deflate;q=0">
81 <!---end--->
82 <cfhttpparam name="TxType" value="Payment" type="formfield">
83 <cfhttpparam name="VPSProtocol" value="#arguments.VPSProtocol#" type="formfield">
84 <cfhttpparam name="Vendor" value="#arguments.vendor#" type="formfield">
85 <cfhttpparam name="VendorTxCode" value="#arguments.VendorTxCode#" type="formfield">
86 <cfhttpparam name="referrerID" value="#arguments.referrerID#" type="formfield">
87 <cfhttpparam name="Currency" value="#arguments.DefaultCurrency#" type="formfield">
88 <cfhttpparam name="Description" value="#arguments.DefaultDescription#" type="formfield">
89 <cfhttpparam name="Amount" value="#arguments.Amount#" type="formfield">
90 <cfhttpparam name="CardHolder" value="#arguments.CardHolder#" type="formfield">
91 <cfhttpparam name="CardNumber" value="#arguments.CardNumber#" type="formfield">
92 <cfhttpparam name="GiftAidPayment" value="0" type="formfield">
93 <cfhttpparam name="ApplyAVSCV2" value="#arguments.DefaultApplyAVSCV2#" type="formfield">
94 <cfhttpparam name="BillingSurname" value="#arguments.billlastName#" type="formfield">
95 <cfhttpparam name="BillingFirstnames" value="#arguments.billfirstName#" type="formfield">
96 <cfhttpparam name="BillingCity" value="#arguments.billingcity#" type="formfield">
97 <cfhttpparam name="BillingCountry" value="#arguments.BillingCountry#" type="formfield">
98 <cfhttpparam name="DeliverySurname" value="#arguments.billlastName#" type="formfield">
99 <cfhttpparam name="DeliveryFirstnames" value="#arguments.billfirstName#" type="formfield">
100 <cfhttpparam name="DeliveryAddress1" value="#arguments.BillingAddress1#" type="formfield">
101 <cfhttpparam name="DeliveryCity" value="#arguments.BillingAddress1#" type="formfield">
102 <cfhttpparam name="DeliveryCountry" value="#arguments.BillingCountry#" type="formfield">
103 <cfhttpparam name="Basket" value="#arguments.Basket#" type="formfield">
104 <cfif #arguments.StartDate# is not "">
105 <cfhttpparam name="StartDate" value="#arguments.StartDate#" type="formfield">
106 </cfif>
107 <cfif #arguments.ExpiryDate# is not "">
108 <cfhttpparam name="ExpiryDate" value="#arguments.ExpiryDate#" type="formfield">
109 </cfif>
110 <cfif #arguments.DeliveryAddress# is not "">
111 <cfhttpparam name="DeliveryAddress" value="#arguments.DeliveryAddress#" type="formfield">
112 </cfif>
113 <cfhttpparam name="BillingAddress1" value="#arguments.BillingAddress1#" type="formfield">
114 <cfif #arguments.IssueNumber# is not "">
115 <cfhttpparam name="IssueNumber" value="#arguments.IssueNumber#" type="formfield">
116 </cfif>
117 <cfhttpparam name="CV2" value="#arguments.CV2#" type="formfield">
118 <cfhttpparam name="CardType" value="#arguments.CardType#" type="formfield">
119 <cfhttpparam name="BillingPostCode" value="#arguments.BillingPostCode#" type="formfield">
120 <cfif #arguments.DeliveryPostCode# is not "">
121 <cfhttpparam name="DeliveryPostCode" value="#arguments.DeliveryPostCode#" type="formfield">
122 </cfif>
123 <cfhttpparam name="CustomerName" value="#arguments.CustomerName#" type="formfield">
124 <cfif #arguments.ContactNumber# is not "">
125 <cfhttpparam name="ContactNumber" value="#arguments.ContactNumber#" type="formfield">
126 </cfif>
127 <cfif #arguments.ContactFax# is not "">
128 <cfhttpparam name="ContactFax" value="#arguments.ContactFax#" type="formfield">
129 </cfif>
130 <cfhttpparam name="CustomerEmail" value="#arguments.CustomerEmail#" type="formfield">
131 <cfif #arguments.ClientIPAddress# is not "">
132 <cfhttpparam name="ClientIPAddress" value="#arguments.ClientIPAddress#" type="formfield">
133 </cfif>
134 <cfif #arguments.CAVV# is not "">
135 <cfhttpparam name="CAVV" value="#arguments.CAVV#" type="formfield">
136 </cfif>
137 <cfif #arguments.XID# is not "">
138 <cfhttpparam name="XID" value="#arguments.XID#" type="formfield">
139 </cfif>
140 <cfif #arguments.ECI# is not "">
141 <cfhttpparam name="ECI" value="#arguments.ECI#" type="formfield">
142 </cfif>
143 <cfif #arguments.DSecureStatus# is not "">
144 <cfhttpparam name="3DSecureStatus" value="#arguments.arguments.DSecureStatus#" type="formfield">
145 </cfif>
146 </cfhttp>
147 <!--- ********************************END OF HTTPS POST TO PROTX******************************************--->
148 <cfset Response = StructNew()>
149 <!---if http post was ok--->
150 <cfif #cfhttp.statusCode# is "200 OK">
151 <cfloop list="#CFHTTP.FileContent#" index="line" delimiters="#chr(13)#">
152 <cfset line = Trim( line )>
153 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), Trim(Mid(line,Find("=",line)+1,len(line)) ) )>
154 </cfloop>
155 <!---if could not contact gateway--->
156 <cfelse>
157 <cfset StructInsert(Response, "Status", "timeout")>
158 <cfset StructInsert(Response, "StatusDetail", "Timeout Error: could not contact payment gateway or header code was not 200, please contact website owner.")>
159 </cfif>
160 <!---retrun responce--->
161 <cfreturn Response>
162</cffunction>
163<!---3D Secure - New responce to sort out with 3D Secure info in it. sort ready to determine next action--->
164<cffunction name="DSecureCallBack" access="public" hint="Make Call" returntype="struct">
165<cfargument name="MD" type="string" required="yes">
166<cfargument name="PARes" type="string" required="yes">
167<cfscript>
168var Response= 0;
169</cfscript>
170 <cfhttp url="#GatewaySettings.callbackURL#" method="post" delimiter="," throwonerror="no">
171 <cfhttpparam type="Header" name="charset" value="utf-8" />'<!---had some issues on CF8 with compression MX7 seem to be fine--->
172 <cfhttpparam name="MD" value="#arguments.MD#" type="formfield">
173 <cfhttpparam name="PARes" value="#arguments.PARes#" type="formfield">
174 </cfhttp>
175 <cfset Response = StructNew()>
176 <cfloop list="#CFHTTP.FileContent#" index="line" delimiters="#chr(13)#">
177 <cfset line = Trim( line )>
178 <cfset StructInsert( Response, Trim( ListFirst( line, "=" ) ), Trim(Mid(line,Find("=",line)+1,len(line)) ) )>
179 </cfloop>
180 <!---retrun responce--->
181 <cfreturn Response>
182</cffunction>
183<!------------------------------------------- PRIVATE ------------------------------------------->
184<cffunction name="setEnvironment" access="private" hint="Set which gateway ULR's are to be used, simulator, test or live" output="false" returntype="struct">
185<!--- ************************************************************* --->
186<cfargument name="gateway" type="string" required="true" default="simulator"/>
187<!--- ************************************************************* --->
188<cfset GatewaySettings = StructNew() />
189<cfscript>
190 if (arguments.gateway is "simulator") {
191 StructInsert(GatewaySettings, "Verify", "false");
192 StructInsert(GatewaySettings, "PurchaseURL", "https://test.sagepay.com/Simulator/VSPDirectGateway.asp");
193 StructInsert(GatewaySettings, "RefundURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorRefundTx");
194 StructInsert(GatewaySettings, "ReleaseURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorReleaseTx");
195 StructInsert(GatewaySettings, "RepeatURL", "https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorRepeatTx");
196 StructInsert(GatewaySettings, "callbackURL", "https://test.sagepay.com/Simulator/VSPDirectCallback.asp");
197 }
198 if (arguments.gateway is "test") {
199 StructInsert(GatewaySettings, "Verify", "false");
200 StructInsert(GatewaySettings, "PurchaseURL", "https://test.sagepay.com/gateway/service/vspdirect-register.vsp");
201 StructInsert(GatewaySettings, "RefundURL", "https://test.sagepay.com/gateway/service/refund.vsp");
202 StructInsert(GatewaySettings, "ReleaseURL", "https://test.sagepay.com/gateway/service/release.vsp");
203 StructInsert(GatewaySettings, "RepeatURL", "https://test.sagepay.com/gateway/service/repeat.vsp");
204 StructInsert(GatewaySettings, "callbackURL", "https://test.sagepay.com/gateway/service/direct3dcallback.vsp");
205 }
206 if (arguments.gateway is "live") {
207 StructInsert(GatewaySettings, "Verify", "false");
208 StructInsert(GatewaySettings, "PurchaseURL", "https://live.sagepay.com/gateway/service/vspdirect-register.vsp");
209 StructInsert(GatewaySettings, "RefundURL", "https://live.sagepay.com/gateway/service/refund.vsp");
210 StructInsert(GatewaySettings, "ReleaseURL", "https://live.sagepay.com/gateway/service/release.vsp");
211 StructInsert(GatewaySettings, "RepeatURL", "https://live.sagepay.com/gateway/service/repeat.vsp");
212 StructInsert(GatewaySettings, "callbackURL", "https://live.sagepay.com/gateway/service/direct3dcallback.vsp");
213 }
214</cfscript>
215 <cfreturn GatewaySettings>
216</cffunction>
217
218</cfcomponent>
How to use
In your ColdBox config setup a new custom setting like so
2 <!--simulator,test or live can be used -->
3 <Setting name="gatewayEnvironment" value="simulator"/>
4 </YourSettings>
Create your payment pages/forms (example will be posted soon)
When you make the call it will look something like this
2 SagePayPlugin = getPlugin(plugin="SagePay",customPlugin=true,newInstance=true);
3
4//SagePay gateway CFHTTP call
5response = SagePayPlugin.makeCall(
6VendorTxCode = "", //you transaction code has to be unique for each call
7vendor = "something",//your vendor name
8VPSProtocol = "2.23",
9referrerID = "someone",
10amount=rc.Amount,//transaction account
11CardHolder=rc.cardHolder,//customer name and name on card
12CardNumber=cardNumber,//card number
13StartDate=rc.startDate1&rc.startDate2,//card start date
14ExpiryDate=rc.expiryDate1&rc.expiryDate2,//card expiry date
15DeliveryAddress=rc.billaddress1,//DeliveryAddress same as billing address
16CardType=rc.cardtype,//type of card being used
17BillingPostCode=rc.billPostCode,//card post code
18DeliveryPostCode=rc.billPostCode,//DeliveryAddress same as billing address
19CustomerName=rc.cardHolder,//card holders name
20CV2=rc.CV2,//security codebillfirstName=rc.fname,//billing first name
21billlastName=rc.lname,//billing last name
22BillingAddress1=rc.billaddress1,//billing address
23billingcity=rc.billcity//billing city
24);
Handling the response
2
3
4}
5
6 else if (response.status IS "3DAUTH"){
7
8newresponse = SagePayPlugin.DSecureCallBack(MD=rc.md,
9 PARes=rc.PARes
10 );
11
12}
13
14 else {
15 getPlugin("messagebox").setMessage("error",Response.Status&"<br />"&Response.StatusDetail);
16 }
Jun21



22/06/09 19:57
I have used protx/sagepay vsp server, always been a little put off by vsp direct as you need your own cert. but this looks really good and well put together. It also seems to be the only example I can find in coldfusion at the mo, thanks for this it really will be a help.