Search code examples
iphonepythonhttpsin-app-purchasetwisted

Apple store receipt validation through Twisted server


I'm trying to validate a transaction receipt from an inApp purchase with the Apple store server from my Twisted server. I have sent the (SKPaymentTransaction *)transaction.transactionReceipt from my app to my server.

But now, sending the JSON object to the Apple server, I keep getting an unhandled error in Deferred from my Agent.request(). I suspect this is because I'm not listening on port 443 for response from Apple store, but I don't want my app to communicate with my Twisted server on port 443 also. Here is my code:

from twisted.application import internet, service
from twisted.internet import protocol, reactor
from zope.interface import implements
from twisted.web.iweb import IBodyProducer

from twisted.internet import defer
from twisted.web.client import Agent
from twisted.web.http_headers import Headers
import json
import base64

class StringProducer(object):
    implements(IBodyProducer)

    def __init__(self, body):
        self.body = body
        self.length = len(body)

    def startProducing(self, consumer):
        consumer.write(self.body)
        return succeed(None)

    def pauseProducing(self):
        pass

    def stopProducing(self):
        pass

def printResponse(response):
    print response       # just testing to see what I have

def httpRequest(url, values, headers={}, method='POST'):
    agent = Agent(reactor)
    d = agent.request(method,
                      url,
                      Headers(headers),
                      StringProducer(values)
                      )
    d.addCallback(printResponse)

class storeServer(protocol.Protocol):

    def dataReceived(self, data):
        receiptBase64 = base64.standard_b64encode(data)
        jsonReceipt = json.dumps({'receipt-data':receiptBase64})
        print jsonReceipt     # verified that my data is correct

        d = httpRequest(
            "https://buy.itunes.apple.com/verifyReceipt",
            jsonReceipt,
            {'Content-Type': ['application/x-www-form-urlencoded']}
            )

factory = protocol.Factory()
factory.protocol = storeServer
tcpServer = internet.TCPServer(30000, factory)
tcpServer.setServiceParent(application)

How can I fix this error? Do I have to create another service listening on port 443? If so, how might I have the service connecting to my app communicate with the service connecting through https?


Solution

  • The comment style in your code sample is incorrect. Python uses # for comments, not //.

    After fixing that and running the snippet through pyflakes, I see these errors:

    program.py:1: 'service' imported but unused
    program.py:6: 'defer' imported but unused
    program.py:21: undefined name 'succeed'
    program.py:48: local variable 'd' is assigned to but never used
    program.py:57: undefined name 'application'
    

    It seems likely that the undefined name on line 21 is the cause of the NameError you've encountered. NameError is how Python signals this sort of bug:

    x = y
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'y' is not defined