Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to work with subscriptions, recurrent payments and transactions without leaving the site's page? #9

Open
martinlombana opened this issue Aug 5, 2020 · 2 comments

Comments

@martinlombana
Copy link

Just wondering if this 3 things are implemented,

Thanks!

@ddiazpinto
Copy link
Owner

Recently I started to develop a deep revision of this module that includes REST integration (tokenization, 1 click payment, etc) but right now it is just a WIP. I'm afraid I can't give an estimation of time to complete these features due to a lack of time.

@Malberix
Copy link

Malberix commented Aug 6, 2021

Hi. My client need to implement suscriptions so I did some "homemade" changes over your package. In order to thank your work, I'll like to share my discoveries and changes with you. Maybe can be useful with your enhacement task, but notice that this procedure needs also changes in local service logic to work and a daemon or manual process to generate the "automatic" calls.
Of course, it also can be used as intended 1-click payments with user action.

I've read related doc of Redsys and I found two ways: Payment by Reference and COF (Credential on file). I think the best way to do this task is to pay with reference, because the COF payment has restrictions to 12 payments and check frecuency. With reference you can send a payment whenever you need.

To make this work, in the first payment that you will process as usual, you have to include the variable
"DS_MERCHANT_IDENTIFIER": "REQUIRED" (need to modify request.py to add this).

When the payment is done, you will receive the var "DS_MERCHANT_IDENTIFIER" with a key value you have to store anywhere related to your client account. You'll also receive "Ds_ExpiryDate" wich indicates the date until you can send payments with this reference. (need to modify response.py to add this variables).

Now you should be able to send payments and Redsys recomends to do this through WebService (SOAP).
There are two things you need to know to do this: How to compose the signature and the data you need to send.
It was a hard work for me to discover the SOAP data. I have used a RAW for SOAP-XML input but sure it could be formated as a dict in the same way you do with usual payments and formated.

The signature uses an input string wich is (UPPER case is mandatory or Webservice won't recognize the variables):
datosEntrada = """
<DS_MERCHANT_MERCHANTCODE>........</DS_MERCHANT_MERCHANTCODE>
<DS_MERCHANT_TRANSACTIONTYPE>0</DS_MERCHANT_TRANSACTIONTYPE>
<DS_MERCHANT_ORDER>"""+str(order_no)+"""</DS_MERCHANT_ORDER>
<DS_MERCHANT_TERMINAL>001</DS_MERCHANT_TERMINAL>
<DS_MERCHANT_CURRENCY>"""+str(EUR)+"""</DS_MERCHANT_CURRENCY>
<DS_MERCHANT_AMOUNT>"""+str(round(amount*100,0))+"""</DS_MERCHANT_AMOUNT>
<DS_MERCHANT_IDENTIFIER>"""+merchant_identifier+"""</DS_MERCHANT_IDENTIFIER>
"""

In difference with "usual" form payment, You DON'T HAVE to convert to base64 the parameters string when getting de HMAC256. so...
I created the function in client.py:
def generate_signature_ws2(self, order: str, xmldata: str) -> bytes:
return self.sign_hmac256(self.encrypt_3DES(order), xmldata.encode())

Notice I don't call to client.prepare_request(parameters) because I don't prepare parameters in this case. (but sure you can develope and automate it in order to a cleaner code)

Now you can generate de "BODY" of the SOAP-XML (includes your merchant_identifier (reference) ):

xml = """<REQUEST>"""
datosEntrada = """<DATOSENTRADA>
                        <DS_MERCHANT_MERCHANTCODE>......</DS_MERCHANT_MERCHANTCODE>
                        <DS_MERCHANT_TRANSACTIONTYPE>0</DS_MERCHANT_TRANSACTIONTYPE>
                        <DS_MERCHANT_ORDER>"""+str(order_no)+"""</DS_MERCHANT_ORDER>
                        <DS_MERCHANT_TERMINAL>001</DS_MERCHANT_TERMINAL>
                        <DS_MERCHANT_CURRENCY>"""+str(EUR)+"""</DS_MERCHANT_CURRENCY>
                        <DS_MERCHANT_AMOUNT>"""+str(round(amount*100,0))+"""</DS_MERCHANT_AMOUNT>
                        <DS_MERCHANT_IDENTIFIER>"""+**merchant_identifier**+"""</DS_MERCHANT_IDENTIFIER>
                    </DATOSENTRADA>"""                    
signature = client.generate_signature_ws2(str(order_no), datosEntrada).decode('utf-8')
xml += datosEntrada
xml += """<DS_SIGNATUREVERSION>HMAC_SHA256_V1</DS_SIGNATUREVERSION>
            <DS_SIGNATURE>""" + signature +"""</DS_SIGNATURE>
        </REQUEST>"""

Finally, the webservice needs an input just like:
soap = """
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservice.sis.sermepa.es">
soapenv:Header/
soapenv:Body
web:trataPeticion
web:datoEntrada
< ![CDATA["""+xml+"""]]> ##REMOVE THE SPACE BETWEEN < AND !
</web:datoEntrada>
</web:trataPeticion>
</soapenv:Body>
</soapenv:Envelope>"""

All this code should be part of new functions in requests.py with recieve a simple dict and returns the final string.

Now you can do a request with the lib requests adding the mandatory headers;
url = "https://sis-t.redsys.es:25443/sis/services/SerClsWSEntrada" ##PRUEBAS
headers = {'content-type': 'text/xml',
'SOAPAction': """",
}

f =  requests.post(url, data=soap,  headers=headers)

Now you can get the response which is HTML encoded:
print ('status: ' + str(f.status_code))

Also the REDSYS return code... (it's an XML and you can parse the way you need.. in my homemade case):
xml = f.text.replace("<","<").replace("&amp","&").replace(">",">").replace('\n','').replace('\n','').replace(" ","")
pos_ini = xml.find('')
pos_fin = xml.find('')
print (" pos_ini: " + str(pos_ini))
codigo = xml[int(pos_ini)+8:int(pos_fin)]

print ("CODIGO DE RETORNO DE REDSYS: " + codigo)

Surely, serverside must do things in case this code is 0 (or not).. just like the function that recieves URL_OKL/URL_KO when redirect.

Hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants