You can implement a (one-way, two-step) escrow that works like
* you advertise some data, perhaps a zero-knowledge proof that you have it
* buyer pays money into the escrow
* you send data off-chain to buyer
* buyer examines data and if satisfactory, releases the payment to you
Once the buyer commits their funds, there's no way for them to get it back,
but they can withhold payment as leverage to make sure you provide the
promised data.
In Solidity pseudo-code, it would look similar to
contract Escrow {
mapping(address => uint256) public payments;
mapping(address => uint256) public released;
mapping(address => uint256) public transferred;
address public owner;
constructor() public {
owner = msg.sender;
}
function payInto() payable {
require(msg.value > 0, 'Sender attempted to pay zero amount.');
require(payments[msg.sender] == 0, 'Sender previously paid into escrow.');
payments[msg.sender] = msg.value;
}
function release() {
require(payments[msg.sender] > 0, 'Sender has not paid into escrow.');
require(released[msg.sender] == 0, 'Sender has already released funds.');
released[msg.sender] = payments[msg.sender];
}
function safeWithdraw(address buyer) {
require(msg.sender == owner, 'Non-owner tried to withdraw funds.');
require(released[buyer] > 0, 'No released funds from buyer to withdraw.');
require(transferred[buyer] == 0, 'Re-entrancy attempt during withdrawal.');
transferred[buyer] = released[buyer];
owner.transfer(released[buyer]);
}
}