Tuesday, November 19, 2013

grails deleted object would be re-saved by cascade error

have the 'User' as set-up below. I am trying to delete a 'User' by user.delete(flush: true), and I get the following error:
org.hibernate.ObjectDeletedException: deleted object would be re-saved by 
cascade (remove deleted object from associations): [User#2]
 


class User {
    Integer id;
    String name;

    static belongsTo = [
            role: Role
    ]

    static constraints = {

    }
}


class Role {
    Integer id;

    static hasMany = [
            userList: User
    ]

    static constraints = {

    }
}

To make things short: Hibernate, and so Grails will want to save() the role object at the end of the Hibernate session (in your case at the .delete(flush:true) call) because it detects that the object has been modified (a user has been suppressed). And the role must have kept a link toward the user, causing Hibernate to feel you will delete() the user to save() it back again.
To workaround this:
 def role = user.role; 
 role.discard(); 
 user.delete(flush:true);
This tell Hibernate not to save() the role without you asking for it.

Test Credit Card Account Numbers

The table below contains a number of credit card numbers that can be used to test credit card handling software.

4111111111111111   (VISA)
4222222222222    (VISA)
4444333322221111   (VISA)
378282246310005   (AMEX)
378734493671000   (AMEX)
371449635398431   (AMEX)
6011111111111117   (DISCOVER)
6011000990139424   (DISCOVER)
5555555555554444   (MASTERCARD)
5105105105105100   (MASTERCARD)

Monday, November 18, 2013

Token or crn xml payment using NAB

Download for payment response code

testUrl = "https://transact.nab.com.au/test/xmlapi/payment";
liveUrl = "https://transact.nab.com.au/xmlapi/payment";

NAB Transact XML API  Public Test Account Details
Merchant ID: XYZ0010
Transaction Password: abcd1234 


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xmlparser;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *
 * @author Pritom K Mondal
 */
public class NabTokenPayment {
    public static void main(String[] args) throws Exception {
        NabTokenPayment nabTokenPayment = new NabTokenPayment();
        nabTokenPayment.pay();
    }
    
    private void pay() throws Exception {
        String testURL = "https://transact.nab.com.au/xmlapidemo/periodic";
        String liveURL = "https://transact.nab.com.au/xmlapi/periodic";
        String xml = getTokenPaymentXml();
        URL url = new URL(testURL); 
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();           
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setInstanceFollowRedirects(false); 
        connection.setRequestMethod("POST"); 
        connection.setRequestProperty("Content-Type", "text/xml"); 
        connection.setRequestProperty("charset", "utf-8");
        connection.setRequestProperty("Content-Length", "" + Integer.toString(xml.getBytes().length));
        connection.setUseCaches (false);

        DataOutputStream wr = new DataOutputStream(connection.getOutputStream ());
        wr.writeBytes(xml);
        wr.flush();
        wr.close();
        
        System.out.println("Response code from nab: " + connection.getResponseCode());

        BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String line, responseText = "";
        while ((line = br.readLine()) != null) {
            responseText += line;
        }
        br.close();
        
        System.out.println("Response: \n" + responseText);
    }
    
    private String getTokenPaymentXml() throws Exception {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ");
        String messageTimestamp = dateFormat.format(new Date());
        String messageID = MD5(messageTimestamp);
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<NABTransactMessage>\n" +
                "<MessageInfo>\n" +
                    "<messageID>" + messageID + "</messageID>\n" +
                    "<messageTimestamp>" + messageTimestamp + "</messageTimestamp>\n" +
                    "<timeoutValue>60</timeoutValue>\n" +
                    "<apiVersion>spxml-4.2</apiVersion>\n" +
                "</MessageInfo>\n" +
                "<MerchantInfo>\n" +
                    "<merchantID>XYZ0010</merchantID>\n" +
                    "<password>abcd1234</password>\n" +
                "</MerchantInfo>\n" +
                "<RequestType>Periodic</RequestType>\n" +
                "<Periodic>\n" +
                    "<PeriodicList count=\"1\">\n" +
                        "<PeriodicItem ID=\"1\">\n" +
                            "<actionType>trigger</actionType>\n" +
                            "<periodicType>8</periodicType>\n" +
                            "<crn>47e8cb6ddc6ba08890d4</crn>\n" +
                            "<transactionReference>Test Trigger CC Payment</transactionReference>\n" +
                            "<amount>1200</amount>\n" +
                            "<currency>AUD</currency>\n" +
                        "</PeriodicItem>\n" +
                    "</PeriodicList>\n" +
                "</Periodic>\n" +
            "</NABTransactMessage>";
        return xml;
    }
    
    private String convertedToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();       
        for (int i = 0; i < data.length; i++) {
            int halfOfByte = (data[i] >>> 4) & 0x0F;
            int twoHalfBytes = 0;
            do {
                if ((0 <= halfOfByte) && (halfOfByte <= 9)) {
                    buf.append( (char) ('0' + halfOfByte) );
                } else {
                    buf.append( (char) ('a' + (halfOfByte - 10)) );
                }
                halfOfByte = data[i] & 0x0F;
            } while(twoHalfBytes++ < 1);
        }
        return buf.toString();
    }

    public String MD5(String text) 
            throws NoSuchAlgorithmException, UnsupportedEncodingException 
    {
        MessageDigest md;
        md = MessageDigest.getInstance("MD5");
        byte[] md5 = new byte[64];
        md.update(text.getBytes("iso-8859-1"), 0, text.length());
        md5 = md.digest();
        return convertedToHex(md5);
    }
}

Successful response:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NABTransactMessage>
    <MessageInfo>
        <messageID>af7296fcab496a96cf54aef73ff66960</messageID>
        <messageTimestamp>20131911003610525000+660</messageTimestamp>
        <apiVersion>spxml-4.2</apiVersion>
    </MessageInfo>
    <MerchantInfo>
        <merchantID>XYZ0010</merchantID>
    </MerchantInfo>
    <Status>
        <statusCode>0</statusCode>
        <statusDescription>Normal</statusDescription>
    </Status>
    <RequestType>Periodic</RequestType>
    <Periodic>
        <PeriodicList count="1">
            <PeriodicItem ID="1">
                <actionType>trigger</actionType>
                <crn>47e8cb6ddc6ba08890d4</crn>
                <responseCode>00</responseCode>
                <responseText>Approved</responseText>
                <successful>yes</successful>
                <periodicType>8</periodicType>
                <amount>1200</amount>
                <currency>AUD</currency>
                <txnID>983211</txnID>
                <transactionReference>Test Trigger CC Payment</transactionReference>
                <settlementDate>20131119</settlementDate>
                <CreditCardInfo>
                    <pan>444433...111</pan>
                    <expiryDate>09/15</expiryDate>
                    <cardType>6</cardType>
                    <cardDescription>Visa</cardDescription>
                </CreditCardInfo>
            </PeriodicItem>
        </PeriodicList>
    </Periodic>
</NABTransactMessage>

Error response:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NABTransactMessage>
    <MessageInfo>
        <messageID>f8c4385e4beb4a7c177e8b8aaa124e15</messageID>
        <messageTimestamp>20131911004348303000+660</messageTimestamp>
        <apiVersion>spxml-4.2</apiVersion>
    </MessageInfo>
    <MerchantInfo>
        <merchantID>XYZ0010</merchantID>
    </MerchantInfo>
    <Status>
        <statusCode>0</statusCode>
        <statusDescription>Normal</statusDescription>
    </Status>
    <RequestType>Periodic</RequestType>
    <Periodic>
        <PeriodicList count="1">
            <PeriodicItem ID="1">
                <actionType>trigger</actionType>
                <crn>47e8cb6ddc6ba08890d4</crn>
                <responseCode>04</responseCode>
                <responseText>Pick Up Card</responseText>
                <successful>no</successful>
                <periodicType>8</periodicType>
                <amount>304</amount>
                <currency>AUD</currency>
                <txnID>983222</txnID>
                <transactionReference>Test Trigger CC Payment</transactionReference>
                <settlementDate>20131119</settlementDate>
                <CreditCardInfo>
                    <pan>444433...111</pan>
                    <expiryDate>09/15</expiryDate>
                    <cardType>6</cardType>
                    <cardDescription>Visa</cardDescription>
                </CreditCardInfo>
            </PeriodicItem>
        </PeriodicList>
    </Periodic>
</NABTransactMessage>

Wednesday, November 13, 2013

Create/edit token/crn for NAB transact

testUrl = "https://transact.nab.com.au/xmlapidemo/periodic";
liveUrl = "https://transact.nab.com.au/xmlapi/periodic";
NAB Transact XML API  Public Test Account Details
Merchant ID: XYZ0010
Transaction Password: abcd1234 


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xmlparser;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *
 * @author Pritom K Mondal
 */
public class NabTokenCreate {
    public static void main(String[] args) throws Exception {
        NabTokenCreate tokenCreate = new NabTokenCreate();
        tokenCreate.createToken();
    }
    
    void createToken() throws Exception {
        String testURL = "https://transact.nab.com.au/xmlapidemo/periodic";
        String liveURL = "https://transact.nab.com.au/xmlapi/periodic";
        String xml = getTokenXml();
        
        URL url = new URL(testURL); 
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();           
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setInstanceFollowRedirects(false); 
        connection.setRequestMethod("POST"); 
        connection.setRequestProperty("Content-Type", "text/xml"); 
        connection.setRequestProperty("charset", "utf-8");
        connection.setRequestProperty("Content-Length", "" + Integer.toString(xml.getBytes().length));
        connection.setUseCaches (false);

        DataOutputStream wr = new DataOutputStream(connection.getOutputStream ());
        wr.writeBytes(xml);
        wr.flush();
        wr.close();
        
        System.out.println("Response code from nab: " + connection.getResponseCode());

        BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String line, responseText = "";
        while ((line = br.readLine()) != null) {
            responseText += line;
        }
        br.close();
        
        System.out.println("Response: " + responseText);
    }
    
    String getTokenXml() throws Exception {
        String xml = "";
        
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ");
        String messageTimestamp = dateFormat.format(new Date());
        String messageID = MD5(messageTimestamp);
        
        /**
         * crn is the key of the token system of NAB transact.
         * It must be a unique key against one account.
         * And must be less than 20 characters length.
         * Can contains a-z, A-Z, 0-9, space, underscore.
         * To edit existing customer in NAB account, please 
         * find the block '<actionType>addcrn</actionType>' in xml below
         * and replace 'addcrn' with 'editcrn' and please provide 
         * a valid crn existing against provided merchant account.
         */
        String crn = messageID;
        if(crn.length() > 20) {
            crn = crn.substring(0, 20);
        }
        
        xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<NABTransactMessage>\n" +
                "<MessageInfo>\n" +
                    "<messageID>" + messageID + "</messageID>\n" +
                    "<messageTimestamp>" + messageTimestamp + "</messageTimestamp>\n" +
                    "<timeoutValue>60</timeoutValue>\n" +
                    "<apiVersion>spxml-4.2</apiVersion>\n" +
                "</MessageInfo>\n" +
                "<MerchantInfo>\n" +
                    "<merchantID>XYZ0010</merchantID>\n" +
                    "<password>changeit</password>\n" +
                "</MerchantInfo>\n" +
                "<RequestType>Periodic</RequestType>\n" +
                "<Periodic>\n" +
                    "<PeriodicList count=\"1\">\n" +
                        "<PeriodicItem ID=\"1\">\n" +
                            "<actionType>addcrn</actionType>\n" +
                            "<periodicType>5</periodicType>\n" +
                            "<crn>" + crn + "</crn>\n" +
                            "<CreditCardInfo>\n" +
                                "<cardNumber>4444333322221111</cardNumber>\n" +
                                "<expiryDate>09/15</expiryDate>\n" +
                            "</CreditCardInfo>\n" +
                        "</PeriodicItem>\n" +
                    "</PeriodicList>\n" +
                "</Periodic>\n" +
            "</NABTransactMessage>";
        
        return xml;
    }
    
    private String convertedToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();       
        for (int i = 0; i < data.length; i++) {
            int halfOfByte = (data[i] >>> 4) & 0x0F;
            int twoHalfBytes = 0;
            do {
                if ((0 <= halfOfByte) && (halfOfByte <= 9)) {
                    buf.append( (char) ('0' + halfOfByte) );
                } else {
                    buf.append( (char) ('a' + (halfOfByte - 10)) );
                }
                halfOfByte = data[i] & 0x0F;
            } while(twoHalfBytes++ < 1);
        }
        return buf.toString();
    }

    public String MD5(String text) 
            throws NoSuchAlgorithmException, UnsupportedEncodingException 
    {
        MessageDigest md;
        md = MessageDigest.getInstance("MD5");
        byte[] md5 = new byte[64];
        md.update(text.getBytes("iso-8859-1"), 0, text.length());
        md5 = md.digest();
        return convertedToHex(md5);
    } 
}

Successful response from NAB as:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NABTransactMessage>
    <MessageInfo>
        <messageID>5fc6a6428d5dd433c7081e97ab92697d</messageID>
        <messageTimestamp>20131411021406034000+660</messageTimestamp>
        <apiVersion>spxml-4.2</apiVersion>
    </MessageInfo>
    <MerchantInfo>
        <merchantID>XYZ0010</merchantID>
    </MerchantInfo>
    <Status>
        <statusCode>0</statusCode>
        <statusDescription>Normal</statusDescription>
    </Status>
    <RequestType>Periodic</RequestType>
    <Periodic>
        <PeriodicList count="1">
            <PeriodicItem ID="1">
                <actionType>addcrn</actionType>
                <crn>5fc6a6428d5dd433c708</crn>
                <responseCode>00</responseCode>
                <responseText>Successful</responseText>
                <successful>yes</successful>
                <DirectEntryInfo>
                    <bsbNumber/>
                    <accountNumber/>
                    <accountName/>
                    <creditFlag>no</creditFlag>
                </DirectEntryInfo>
                <CreditCardInfo>
                    <pan>444433...111</pan>
                    <expiryDate>09/15</expiryDate>
                    <recurringFlag>no</recurringFlag>
                </CreditCardInfo>
                <currency>AUD</currency>    
                <periodicType>5</periodicType>
                <paymentInterval/>
                <numberOfPayments/>
            </PeriodicItem>
        </PeriodicList>
    </Periodic>
</NABTransactMessage>

Unsuccessful response from NAB as


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NABTransactMessage>
    <MessageInfo>
        <messageID>9efcd6fe01a0d4da7c69e16b2583ba44</messageID>
        <messageTimestamp>20131411022423765000+660</messageTimestamp>
        <apiVersion>spxml-4.2</apiVersion>
    </MessageInfo>
    <MerchantInfo>
        <merchantID>XYZ0010</merchantID>
    </MerchantInfo>
    <Status>
        <statusCode>0</statusCode>
        <statusDescription>Normal</statusDescription>
    </Status>
    <RequestType>Periodic</RequestType>
    <Periodic>
        <PeriodicList count="1">
            <PeriodicItem ID="1">
                <actionType>addcrn</actionType>
                <crn>9efcd6fe01a0d4da7c69</crn>
                <responseCode>301</responseCode>
                <responseText>Invalid Credit Card Number</responseText>
                <successful>no</successful>
                <DirectEntryInfo>
                    <bsbNumber/>
                    <accountNumber/>
                    <accountName/>
                    <creditFlag>no</creditFlag>
                </DirectEntryInfo>
                <CreditCardInfo>
                    <pan>473433...112</pan>
                    <expiryDate/>
                    <recurringFlag>no</recurringFlag>
                </CreditCardInfo>
                <currency>AUD</currency>
                <periodicType>5</periodicType>
                <paymentInterval/>
                <numberOfPayments/>
            </PeriodicItem>
        </PeriodicList>
    </Periodic>
</NABTransactMessage>

Response codes:


000 Normal Message processed correctly (check transaction response for details).

504 Invalid Merchant ID
If Merchant ID does not follow the format XXXDDDD, where X is a letter
and D is a digit, or Merchant ID is not found in NAB Transact’s database.

505 Invalid URL The URL is invalid.

510 Unable To Connect To Server
Produced by NAB Transact Client API when unable to establish connection
to NAB Transact Payment Gateway

511
Server Connection Aborted During
Transaction
Produced by NAB Transact Client API when connection to NAB Transact
Payment Gateway is lost after the payment transaction has been sent

512 Transaction timed out By Client
Produced by NAB Transact Client API when no response to payment
transaction has been received from NAB Transact Payment Gateway within
predefined time period (default 80 seconds)

513 General Database Error Unable to read information from the database.

514 Error loading properties file
Payment Gateway encountered an error while loading configuration
information for this transaction

515 Fatal Unknown Error
Transaction could not be processed by the Payment Gateway due to
unknown reasons

516 Request type unavailable NAB Transact system doesn’t support the requested transaction type

517 Message Format Error
The transaction sent to the NAB Transact Payment Gateway has not been
formatted according to the specifications.

518 Customer Not Registered The Customer cannot be found

524 Response not received The client could not receive a response from the server.

545 System maintenance in progress
The system maintenance is in progress and the system is currently unable
to process transactions

550 Invalid password The merchant has attempted to process a request with an invalid password.

575 Not implemented This functionality has not yet been implemented

577 Too Many Records for Processing
The maximum number of allowed events in a single message has been
exceeded.

580 Process method has not been called The process() method object has not been called

595 Merchant Disabled
NAB Transact has disabled the merchant and the requests from this
merchant will not be processed.

300 Invalid Amount If payment transaction amount is non-integer, negative, or zero.

301 Invalid Credit Card Number
Credit card number is not supplied, wrong length, or does not pass Luhn
algorithm.

302 Invalid Expiry Date
Expiry date does not follow format MM/YY, where MM is the 2-digit month
(01-12) and YY is the 2-digit year.

303 Invalid CRN CRN is not provided or is longer than 20 characters.

304 Invalid Merchant ID
Merchant ID does not follow format XXXDDDD for credit card payments,
or XXXDD for direct entry, where X is a letter and D is a digit; or merchant ID
not in database.

305 Invalid BSB Number BSB does not follow format DDDDDD, or DDD-DDD, where D is a digit.

306 Invalid Account Number
Account number not provided, greater than 9 digits, or contains non-digit
characters.

307 Invalid Account Name
Account Name is mandatory for DE Credit payments, and optional for DE
Debits. Must be less than 32 characters if supplied.

308 No Matching DDA Found
A periodic DE Debit payment must match an existing DDA stored in our
database for the merchant. DDAs can be added via the Merchant Login.
DDA expiry date must be after the final periodic payment date.

309 Invalid CVV Number
CVV is optional for credit card payments, but if provided, must be either
3 or 4 digits.

313 General Database Error
A database error occurred while processing your request. Contact NAB
Transact Service Centre.

314 Unable to Read Properties File
A properties file was not found or could not be read. Contact NAB Transact
Service Centre.

316 Invalid Action Type
The server does not support the action type requested. Check user manual
for allowed values.

318 Unable to Decrypt Account Details
The card number or account details could not be decrypted. Contact NAB
Transact Service Centre.

327 Invalid Periodic Payment Type
The Periodic type requested is not supported. Check user manual for
allowed values.

328 Invalid Periodic Frequency
The Periodic frequency requested is not valid. Check user manual for
allowed values.

329 Invalid Number of Payments
Number of payments must be 1 or more for day-based and calendar-based
payments.

332 Invalid Date Format
A supplied date does not follow format YYYYMMDD, where values conform
to standard calendar rules; or the server could not correctly interpret a date.

333 Triggered Payment Not Found
Triggered payment Client ID requested does not match a stored triggered
payment for the merchant.

346 Duplicate CRN Found The CRN has already been registered for this merchant.

347 Duplicate Allocated Variable Found The Allocated Variable has already been registered for this merchant.

how to replace all characters in a java string

String line = "Pritom K Mondal";
line = line.replaceAll("(?s).", "X");

Will output:
XXXXXXXXXXXXXXX
 
The (?s) doesn't match anything but sets the DOTALL flag.

Java - Replace all non digits with an empty character in a string

String cardNumber = "4444 3333 2222 111");
cardNumber = cardNumber.trim();
cardNumber = cardNumber.replaceAll("\\D+", "");

Output:
4444333322221111

Validate credit card and get card type by java code

import org.apache.commons.validator.GenericValidator

def getCreditCardType(String cardNumber) throws AutobillException {
    cardNumber = "" + cardNumber;
    cardNumber = cardNumber.trim();
    cardNumber = cardNumber.replaceAll("\\D+", "");
    if(!GenericValidator.isCreditCard(cardNumber)) {
        throw new InvalidPropertyException("cardNumber");
    }
    if(cardNumber.matches(("^4[0-9]{12}(?:[0-9]{3})?\$"))) {
        return "VISA";
    } else if(cardNumber.matches(("^5[1-5][0-9]{14}\$"))) {
        return "MASTERCARD";
    } else if(cardNumber.matches(("^3[47][0-9]{13}\$"))) {
        return "AMEX";
    } else if(cardNumber.matches(("^3(?:0[0-5]|[68][0-9])[0-9]{11}\$"))) {
        return "DINERS";
    } else if(cardNumber.matches(("^6(?:011|5[0-9]{2})[0-9]{12}\$"))) {
        return "DISCOVER";
    } else if(cardNumber.matches(("^(?:2131|1800|35\\d{3})\\d{11}\$"))) {
        return "JCB";
    }
    return null;
}